Typescript Fluent API - utelate funksjoner fra returverdien til hver funksjon
Dette spørsmålet bygger på spørsmålet og det aksepterte svaret som finnes i denne lenken.
Jeg vil skrive en klasse for skjemaoppretting, der en underliggende type T brukes til å bestemme hvilke funksjoner som kan kalles. Som et eksempel, si at dette er modellen vi ønsker et skjema for:
interface MyFormModel {
name: string;
options: number[];
isPrimary: boolean;
address: {
street: string;
houseNumber: number;
someArrayThatsRelevantToAddresses: string[];
}
}
Jeg vil kunne lage en avkrysningsboks for egenskapen 'isPrimary', og deretter vil jeg at den returnerte verdien av this ikke skal inkludere metoden som ble brukt for å lage avkrysningsbokser, siden det ikke finnes flere boolske egenskaper igjen å kalle "createCheckbox" for. Jeg holder oversikt over hvilke egenskaper som allerede er brukt ved å bruke en andre typeparameter K. Etter hvert funksjonskall med en nøkkel fra T, oppdateres K slik at vi kan spore hvilke nøkler som allerede er brukt. Her er min implementering:
// T is the model for the form, K will accumulate props from T as we build the form
class FormBuilder<T, K extends Partial<T> = {}> {
buildCheckbox<NewKey extends BooleanKeysOf<T>>(): FormBuilder<T, AddProp<T, K, NewKey>> {
return this as any;
}
buildBlock<NewKey extends CompoundKeysOf<T>>(key: NewKey): FormBuilder<T, AddProp<T, K, NewKey>> {
return this as any;
}
buildInput<NewKey extends StringOrNumberKeysOf<T>>(): FormBuilder<T, AddProp<T, K, NewKey>> {
return this as any;
}
buildArray<NewKey extends ArrayKeysOf<T>>(): FormBuilder<T, AddProp<T, K, NewKey>> {
return this as any;
}
finishForm(): K extends T ? FormForModel<T> : MissingProps<T, K> { // tells us which props are missing!
// use childforms to build a complete form
return 'hello' as any;
}
// Get a FormBuilder that accumulates props to build one of the object properties of the parent form
getChildBuilder<NewKey extends CompoundKeysOf<T>>(): FormBuilder<T[NewKey]> {
return new FormBuilder();
}
}
Dette bruker noen hjelpertyper:
/**
* Utility types
*/
// Get from Type T all of the keys that have values of type SomeType
type KeysOfType<T, SomeType> = { [K in keyof T]: T[K] extends SomeType ? K : never }[keyof T]
// Get all keys that are boolean, or string | number, or arrays, or other objects
type BooleanKeysOf<T> = KeysOfType<T, boolean>;
type StringOrNumberKeysOf<T> = KeysOfType<T, string | number>;
type ArrayKeysOf<T> = KeysOfType<T, any[]>;
type CompoundKeysOf<T> = Exclude<KeysOfType<T, object>, ArrayKeysOf<T>>; // get all props that have object types, except arrays
// a type representing the form we're building
type FormForModel<T> = { [K in keyof T]: {} } };
// Add to K another prop that's a part of T
type AddProp<T, K extends Partial<T>, NewKey extends keyof T> = K & { [S in NewKey]: T[S] };
// Which props are missing from K to fill out T?
type MissingProps<T, K extends Partial<T>> = Omit<T, keyof K>;
Ved å bruke denne tilnærmingen er det ganske enkelt å begrense typeparameteren til hver funksjon til kun nøkler som ikke har blitt kalt før, inntil kall-signaturen blir NewKey extends never og ingen kall kan gjøres.
Jeg vil imidlertid gjerne fjerne en funksjon helt fra typens signatur når den ikke har noen lovlige verdier å kalles med. Jeg forestiller meg noe som dette:
buildCheckbox<NewKey extends Exclude<BooleanKeysOf<T>, keyof K>>(): MaybeExcludeCheckbox<T, K, NewKey> {
return this as any;
}
type MaybeExcludeCheckbox<T, K extends Partial<T>, NewKey extends keyof T> = Exclude<keyof T, NewKey> extends never? Omit<FormBuilder<T, AddProp<T, K, NewKey>>, 'buildCheckbox'> : FormBuilder<T, AddProp<T, K, NewKey>>
Dette sjekker om det finnes noen gjenværende boolske egenskaper, hvis det gjør det returnerer det FormBuilder-typen som før, hvis ikke utelater det 'buildCheckbox'-egenskapen. Imidlertid kan det være tilfeller der jeg vil slå flere funksjoner på eller av. Finnes det en mer konsis måte å legge til/fjerne flere egenskaper hver gang en funksjon kalles?