【发布时间】:2018-02-28 22:24:18
【问题描述】:
我有一个类方法,它接受单个参数作为字符串并返回一个具有匹配type 属性的对象。此方法用于缩小已区分的联合类型,并保证返回的对象始终是具有提供的type 区分值的特定缩小类型。
我正在尝试为此方法提供一个类型签名,该签名将正确地从泛型参数缩小类型,但是我没有尝试将它从可区分联合中缩小,而没有用户明确提供它应该缩小的类型到。这行得通,但很烦人,而且感觉很多余。
希望这个最低限度的复制能说明问题:
interface Action {
type: string;
}
interface ExampleAction extends Action {
type: 'Example';
example: true;
}
interface AnotherAction extends Action {
type: 'Another';
another: true;
}
type MyActions = ExampleAction | AnotherAction;
declare class Example<T extends Action> {
// THIS IS THE METHOD IN QUESTION
doSomething<R extends T>(key: R['type']): R;
}
const items = new Example<MyActions>();
// result is guaranteed to be an ExampleAction
// but it is not inferred as such
const result1 = items.doSomething('Example');
// ts: Property 'example' does not exist on type 'AnotherAction'
console.log(result1.example);
/**
* If the dev provides the type more explicitly it narrows it
* but I'm hoping it can be inferred instead
*/
// this works, but is not ideal
const result2 = items.doSomething<ExampleAction>('Example');
// this also works, but is not ideal
const result3: ExampleAction = items.doSomething('Example');
我还尝试变得聪明,尝试动态构建“映射类型”——这是 TS 中相当新的功能。
declare class Example2<T extends Action> {
doSomething<R extends T['type'], TypeMap extends { [K in T['type']]: T }>(key: R): TypeMap[R];
}
这会产生相同的结果:它不会缩小类型,因为在类型映射 { [K in T['type']]: T } 中,每个计算属性 T 的值不是 K in 迭代的每个属性 但与MyActions union 相同。如果我要求用户提供我可以使用的预定义映射类型,那会起作用,但这不是一个选项,因为在实践中这将是一个非常糟糕的开发人员体验。 (工会很大)
这个用例可能看起来很奇怪。我试图将我的问题提炼成更易使用的形式,但我的用例实际上是关于 Observables 的。如果您熟悉它们,我会尝试更准确地键入ofType operator provided by redux-observable。它基本上是filter() on the type property 的简写。
这实际上与 Observable#filter 和 Array#filter 缩小类型的方式非常相似,但 TS 似乎明白这一点,因为谓词回调具有 value is S 返回值。目前还不清楚我如何在这里调整类似的东西。
【问题讨论】:
标签: typescript typescript-typings discriminated-union