错误描述性很强。
Type '{ kind: "a"; }' is not assignable to type 'T'.
'{ kind: "a"; }' is assignable to the constraint of type 'T',
but 'T' could be instantiated with a different subtype of constraint 'AllKinds'.
即使使用更简单的类型约束也会出现此问题。
function foo<T extends string>(): T {
return 'foo';
}
这里我们会得到以下错误。
Type 'string' is not assignable to type 'T'.
'string' is assignable to the constraint of type 'T',
but 'T' could be instantiated with a different subtype of constraint 'string'.
问题是我们说我们会返回T 类型的东西,但T 与string 的类型不同。是的,T 类型扩展了string,但这意味着T 是string 的子类型。例如,'bar' 类型是string 的子类型。因此,我们可以用'bar' 实例化T。因此,我们希望返回值为'bar',但返回值为'foo'。
解决方案就是不使用泛型。如果您想返回string,那么只需说您要返回string。不要说您正在返回某个子类型 T 或 string 的值。
function foo(): string {
return 'foo';
}
同样,如果您想返回一个AllKinds 类型的值,那么只需说您要返回一个AllKinds 类型的值。不要说您正在返回某个子类型 T 或 AllKinds 的值。
type KindA = {kind:'a'};
type KindB = {kind:'b'};
type KindC = {kind:'c'};
type AllKinds = KindA | KindB | KindC;
function create(kind:AllKinds['kind']): AllKinds {
switch(kind) {
case "a": return {kind:'a'};
case "b": return {kind:'b'};
case "c": return {kind:'c'};
}
}
create("a");
编辑:你需要依赖类型来做你想做的事。 TypeScript 没有依赖类型。但是,您可以创建一个自定义折叠函数来提供额外的类型安全性。
type Kind = 'a' | 'b' | 'c';
type KindA = { kind: 'a' };
type KindB = { kind: 'b' };
type KindC = { kind: 'c' };
type AllKinds = KindA | KindB | KindC;
function foldKind<A, B, C>(a: A, b: B, c: C): (kind: Kind) => A | B | C {
return function (kind: Kind): A | B | C {
switch (kind) {
case 'a': return a;
case 'b': return b;
case 'c': return c;
}
}
}
const create: (kind: Kind) => AllKinds = foldKind<KindA, KindB, KindC>(
{ kind: 'a' },
{ kind: 'b' },
{ kind: 'c' }
);
现在,您只能为'a' 提供KindA 的值,为'b' 提供KindB 的值,为'c' 提供KindC 的值。亲自查看demo。