typescript - Härleda typer med hjälp av egna egenskaper
Jag försöker ta reda på om det finns ett sätt att härleda typer i ett gränssnitt från en implementerings egenskaper.
Förenklat exempel:
interface Options {
type: 'string' | 'number'
demanded?: boolean
}
interface Command {
// The parameter options will contain the interpreted version of the options property
callback: (options: InferOptionTypings<this>) => void
options: { [key: string]: Options }
}
// Infer the options
// { type: 'string, demanded: false} | { type: 'string' } => string | undefined
// { type: 'string, demanded: true } => string
// { type: 'number', demanded: false} | { type: 'number } => number | undefined
// { type: 'number, demanded: true } => number
type InferOptionTypings<_ extends Command> = ... // here i've been stuck for very long
Jag har läst typdefinitionerna för yargs (och detta är uppenbarligen inspirerat av yargs), men jag har inte lyckats lista ut hur man får det att fungera på detta sätt eller vad jag saknar/om detta ens är möjligt.
Exempel på användningsfall:
let command: Command = {
callback: (options) => {
options.a // string
options.b // number | undefined
options.c // string | undefined
options.d // error
},
options: {
a: {
type: 'string',
demanded: true,
},
b: {
type: 'number',
},
a: {
type: 'string',
},
},
}
Svar #1
Det är möjligt, men för att kunna härleda det bör du skapa en funktion.
interface Option {
type: 'string' | 'number'
demanded?: boolean
}
/**
* Translates string type name to actual type
* Logic is pretty straitforward
*/
type TranslateType<T extends Option> =
T['type'] extends 'string'
? string
: T['type'] extends 'number'
? number
: never;
/**
* Check if demanded exists
* if true - apply never, because union of T|never produces T
* if false - apply undefined
*/
type ModifierType<T extends Option> =
T extends { demanded: boolean }
? T['demanded'] extends true
? never
: T['demanded'] extends false
? undefined
: never
: undefined
/**
* Apply TranslateType 'string' -> string
* Apply ModifierType {demanded:fale} -> undefined or never
*/
type TypeMapping<T extends Option> = TranslateType<T> | ModifierType<T>
/**
* Apply all conditions to each option
*/
type Mapping<T> = T extends Record<string, Option> ? {
[Prop in keyof T]: TypeMapping<T[Prop]>
} : never
type Data<Options> = {
callback: (options: Mapping<Options>) => void,
options: Options
}
const command = <
/**
* Infer each option
*/
Options extends Record<string, Option>
>(data: Data<Options>) => data
const result = command({
callback: (options) => {
type a = typeof options.a
type b = typeof options.b
type c = typeof options.c
options.a // string
options.b // number | undefined
options.c // string | undefined
options.d // error
},
options: {
a: {
type: 'string',
demanded: true,
},
b: {
type: 'number',
demanded: false
},
c: {
type: 'string',
},
},
})
Jag lämnade kommentarerna under varje typ av verktyg
Lekplats
UPPDATERING Utan funktion:
type WithoutFunction = Data<{
a: {
type: 'string',
demanded: true,
},
b: {
type: 'number',
demanded: false
},
c: {
type: 'string',
},
}>
