使用自己的属性推断类型
Infer types using own properties
我正在尝试找出是否有一种方法可以从实现的属性推断接口中的类型。
简化示例:
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
我读过 yargs 的类型(这显然是受 yargs 的启发),但我还没有想出如何让它以这种风格工作或者我什至 missing/if是可以的。
示例用例:
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',
},
},
}
这是可能的,但为了推断它,您应该创建一个函数。
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',
},
},
})
我在每种实用程序下留下了评论
更新
无功能:
type WithoutFunction = Data<{
a: {
type: 'string',
demanded: true,
},
b: {
type: 'number',
demanded: false
},
c: {
type: 'string',
},
}>
我正在尝试找出是否有一种方法可以从实现的属性推断接口中的类型。
简化示例:
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
我读过 yargs 的类型(这显然是受 yargs 的启发),但我还没有想出如何让它以这种风格工作或者我什至 missing/if是可以的。
示例用例:
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',
},
},
}
这是可能的,但为了推断它,您应该创建一个函数。
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',
},
},
})
我在每种实用程序下留下了评论
更新 无功能:
type WithoutFunction = Data<{
a: {
type: 'string',
demanded: true,
},
b: {
type: 'number',
demanded: false
},
c: {
type: 'string',
},
}>