tsc - Betingede typer med TypeScript
Si at jeg har dette:
type TypeMapping = {
Boolean: boolean,
String: string,
Number: number,
ArrayOfString: Array<string>,
ArrayOfBoolean: Array<boolean>
}
export interface ElemType {
foo: keyof TypeMapping,
default: valueof TypeMapping
}
i stedet for å bruke any som standard, vil jeg definere det betinget, jeg prøvde dette:
export interface ElemType<T extends TypeMapping> {
foo: keyof T,
default: T
}
men det virker ikke helt riktig, vet noen hvordan man gjør dette på riktig måte?
hvis det ikke er klart, for et hvilket som helst objekt som har typen ElemType, må nøkkelen som foo peker til, samsvare med verdien som foo peker til. For eksempel, dette er gyldig:
{
foo: 'String',
default: 'this is a string'
}
men dette er ikke:
{
foo: 'Boolean',
default: 'this should be a boolean instead'
}
så typen på standardfeltet er betinget av verdien/typen til typefeltet.
Kort sagt, hvis foo er 'ArrayOfBoolean', så bør default være: Array<boolean>. Hvis foo er 'Number', så bør default være number, hvis foo er 'Boolean' så bør default være boolean, osv osv.
Svar #1
Du kan definere ElemType som i Catalyst sitt svar, og deretter bruke en kartlagt type for å ta unionen av ElemType for alle mulige K:
interface ElemType<K extends keyof TypeMapping> {
foo: K;
default: TypeMapping[K];
}
type ElemTypeMap = {[K in keyof TypeMapping]: ElemType<K>};
// type ElemTypeMap = {
// Boolean: {foo: "Boolean", default: boolean},
// String: {foo: "String", default: string},
// ...
// }
type SomeElemType = ElemTypeMap[keyof TypeMapping];
// Look up in ElemTypeMap by all keys and take the union:
// {foo: "Boolean", default: boolean} | {foo: "String", default: string} | ...
Svar #2
Du må fortelle TypeScript å validere det faktiske objektet på en eller annen måte, og du kan egentlig ikke unngå det uten å bruke generics; slik ville jeg gjort det:
type TypeMapping = {
Boolean: boolean;
String: string;
Number: number;
ArrayOfString: Array<string>;
};
export interface ElemType<K extends keyof TypeMapping> {
foo: K;
default: TypeMapping[K];
}
const Elem = <E extends keyof TypeMapping, T extends ElemType<E>>(t: ElemType<E>) => t;
Elem({ foo: "Boolean", default: true }); //yup
Elem({ foo: "Boolean", default: "" }); //nope
