TypeScript:使用受约束的泛型不匹配索引查找

时间:2019-06-27 11:05:29

标签: typescript generics

在下面的示例中,我想不出任何听起来都不合理的情况,因此我希望这是允许的。

任何人都可以澄清为什么不允许这样做吗?

type Foo = { foo: number };
type Bar = { bar: number };

type Tag = 'foo' | 'bar';

type MyObject1 = {
    foo: Foo;
    bar: Bar;
};

type MyObject2 = {
    foo: { value: Foo };
    bar: { value: Bar };
};

<T extends Tag>(t: T) => {
    declare const { value }: MyObject2[T];

    // Unexpected type error
    // Type 'Foo | Bar' is not assignable to type 'MyObject1[T]'.
    const desired: MyObject1[T] = value;
};

Playground link

2 个答案:

答案 0 :(得分:2)

这是Improve soundness of indexed access types的副作用。并不是所有的特定分配都是不合理的,但是由于您将desired键入为MyObject1[T],因此可以根据定义将其分配给类型为MyObject1的对象的属性值。因此,如果允许此分配,则可能导致其他问题:

const example = <T extends Tag>(t: T, { value }: MyObject2[T]) => {
    // Unexpected type error
    // Type 'Foo | Bar' is not assignable to type 'MyObject1[T]'.
    const desired: MyObject1[T] = value; 
    let myObj1!: MyObject1;
    myObj1[t] = desired; // this is where the problem would occur, since T can be a union. 
}

example<Tag>("foo", { value: { bar: 0 }}); // this call brings myObj1 to an invalid state myObj1['foo'] will be of type Bar

MyObject1[T]仍然是可能值的组合并且仅在赋值时出错的选项将产生不幸的结果,即无法表示“可分配为对象值的值”,因此此代码无效(并且无法键入):

const setProperty = <T extends Tag>(t: T, v: MyObject1[T]) => {
    let myObj1!: MyObject1;
    myObj1[t] = v; // This assignment would be invalid if MyObject1[T] was not iself seen as the intersection of all possible values of MyObject1
}

如果要表示对象的可能值的并集,请使用MyObject1[Tag]

const example = <T extends Tag>(t: T, { value }: MyObject2[T]) => {
    const desired: MyObject1[Tag] = value; 
}

根据下一步需要执行的操作,有时可能需要类型断言,但是如果没有更多的代码来确切地了解要执行的操作,则很难判断是否可以表达没有断言或是否没有断言确实是类型安全的,或者仅仅是系统类型的限制。

答案 1 :(得分:0)

暂时不要在代码的那部分使用declare,看来当前TypeScript没有足够的智能来知道您的操作是可以的。您必须手动告诉TypeScript value的类型为MyObject1[T]

const desired: MyObject1[T] = value as MyObject1[T];

参见此playground;我还在那里做了一些小的修改。