tre*_*nik 7 typescript conditional-types
我认为解释我的场景的最好方法是使用代码:
interface IPluginSpec {
name: string;
state?: any;
}
interface IPluginOpts<PluginSpec extends IPluginSpec> {
name: PluginSpec['name'];
// How to require opts.initialState ONLY when PluginSpec['state'] is defined?
initialState: PluginSpec['state'];
}
function createPlugin<PluginSpec extends IPluginSpec>(
opts: IPluginOpts<PluginSpec>,
) {
console.log('create plugin', opts);
}
interface IPluginOne {
name: 'pluginOne';
// Ideally state would be omitted here, but I can also live with having to
// define "state: undefined" in plugins without state
// state: undefined;
}
// Error: Property 'initialState' is missing in type...
createPlugin<IPluginOne>({
name: 'pluginOne',
// How to make initialState NOT required?
// initialState: undefined,
// How to make any non-undefined initialState invalid?
// initialState: 'anything works here',
});
interface IPluginTwo {
name: 'pluginTwo';
state: number;
}
createPlugin<IPluginTwo>({
name: 'pluginTwo',
initialState: 0,
});
Run Code Online (Sandbox Code Playgroud)
您可以使用条件类型执行此操作。有了它,您可以测试该属性是否存在,以及是否具有额外属性:
interface IPluginSpec {
name: string;
state?: any;
}
type IPluginOpts<PluginSpec extends IPluginSpec> = PluginSpec extends Record<'state', infer State> ? {
name: PluginSpec['name'];
initialState: State;
} : {
name: PluginSpec['name']
}
function createPlugin<PluginSpec extends IPluginSpec>(
opts: IPluginOpts<PluginSpec>,
) {
console.log('create plugin', opts);
}
interface IPluginOne {
name: 'pluginOne';
}
// Ok
createPlugin<IPluginOne>({
name: 'pluginOne',
// nothing to add
});
interface IPluginTwo {
name: 'pluginTwo';
state: number;
}
createPlugin<IPluginTwo>({
name: 'pluginTwo',
initialState: 0,
});
Run Code Online (Sandbox Code Playgroud)
对于更可组合的方法,您可以使用具有公共部分的交集,并且每个可选部分都有自己的条件:
interface IPluginSpec {
name: string;
state?: any;
config?: any;
}
type IPluginOpts<PluginSpec extends IPluginSpec> = {
name: PluginSpec['name']
}
& (PluginSpec extends Record<'state', infer State> ? { initialState: State; } : {})
& (PluginSpec extends Record<'config', infer Config> ? { initialConfig: Config; } : {})
Run Code Online (Sandbox Code Playgroud)
条件类型对调用者非常有用。问题是在实现内部,打字稿不能真正推理条件类型(因为T未知)。
最好的解决方案是保留一个公共签名(带条件类型)和一个简化的实现签名(不带条件类型)。这将允许您在没有类型断言的情况下实现函数,同时为调用者提供所需的行为:
function createPlugin<PluginSpec extends IPluginSpec>(opts: IPluginOpts<PluginSpec>)
function createPlugin<PluginSpec extends IPluginSpec>(opts: {
name: string
initalState: PluginSpec['state'],
initialConfig: PluginSpec['config'],
}) {
if (opts.initalState) {
opts
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3323 次 |
| 最近记录: |