TS索引类型-接口中使用索引类型时,编译器无法解析确切值

时间:2019-08-21 10:44:42

标签: typescript

如果我具有以下功能:

function test<T, K extends keyof T>(o: T, propertyValue: T[K], propertyName: K) {
    // ... do something
}

我这样做:

interface Car {
    manufacturer: string;
    model: string;
    year: number;
}
let taxi: Car = {
    manufacturer: 'Toyota',
    model: 'Camry',
    year: 2014
};
test(taxi, 0, 'model');

编译器能够检测到模型属性不是字符串类型,因此测试调用行失败。但是,如果我尝试在像这样的界面中使用索引类型:

export interface Change<T, K extends keyof T = keyof T> {
  timestamp: number;
  field: K;
  value: T[K];
}
const change: Change<Car> = {
  timestamp: 1484684700749,
  field: 'model',
  value: 0
};

它可以编译。即使实际上由字段指定属性,它也允许您将值设置为字符串或数字。我是在做错什么,还是由于编译器的工作原因,在这种情况下是不可能的?

您可以看到实际示例here in ts playground

1 个答案:

答案 0 :(得分:1)

那是因为K可以是T任意键,打字稿不知道是哪一个。

考虑以下示例:

const makeChange = (args: any): Change<Car> => {
  // some logic to return the change here.
}

Change<Car>现在应K使用什么类型。由于field的值是在运行时确定的,因此无法在编译期间分配静态类型。

一种解决方法,可以定义您的Change类型,如下所示:

interface Change<T, K extends keyof T> {
    timestamp: number;
    field: K;
    value: T[K];
}

const change: Change<Car, 'model'> = {
    timestamp: 1484684700749,
    field: 'model',
    value: 0 // not working as expected.
};

您需要在类型中指定字段(通用参数K),才能进行静态类型检查。

如果您在其他部分使用Change接口并且不使用特定字段,则始终可以向函数添加通用类型,如下所示:

const uploadChange = <K extends keyof Car>(change: Change<Car, K>) => {
 // ...
}