TS为什么不让我索引到兼容的对象类型?

时间:2019-06-17 10:24:01

标签: typescript

我有以下类型定义。

type Key<Row> = {
  [P in keyof Row]: Row[P] extends string ? P : never
}[keyof Row];

type ID<Row> = Row[Key<Row>];

type Selected<Row> = {
  selected: boolean;
} & Row;

Key<Row>返回值为字符串的Row的所有属性键。 See this answer了解其构造方式。

ID<Row>Row所有字符串值的并集。它始终是string,但是如果字符串值是字符串常量(例如,在

interface X {
  prop: "abc";
}

ID<X>始终为"abc"

Selected<Row>是任何Row加上属性selected: boolean的交集类型。

上面定义的全部,我不明白为什么下面的函数定义会引发错误:

const getId = <Row>(row: Selected<Row>, key: Key<Row>): ID<Selected<Row>> =>
  row[key];

TS给我的类型错误粘贴在下面,但是我不知道为什么TS不喜欢我的代码。

从我看到的情况来看,row[key]应该总是可以正常工作,因为即使rowSelected<Row>Key<Row>也只包含一组较窄的键,所以键问题是什么呢?进入一个属性比我们实际使用的键更多的对象?

Here is a link to the TS playground with this code showing the error


Type 'Selected<Row>[{ [P in keyof Row]: Row[P] extends string ? P : never; }[keyof Row]]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)] & Row[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<...>["selected"] extends string ? "selected" : never)]'.
  Type 'Selected<Row>[Row[keyof Row] extends string ? keyof Row : never]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)] & Row[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<...>["selected"] extends string ? "selected" : never)]'.
    Type 'Selected<Row>[keyof Row]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)] & Row[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<...>["selected"] extends string ? "selected" : never)]'.
      Type 'Row[string] | Row[number] | Row[symbol]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)] & Row[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<...>["selected"] extends string ? "selected" : never)]'.
        Type 'Row[string]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)] & Row[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<...>["selected"] extends string ? "selected" : never)]'.
          Type 'Row[string]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)]'.
            Type 'Row' is not assignable to type '{ selected: boolean; }'.
              Type 'Row[string]' is not assignable to type 'boolean'.
                Type 'Selected<Row>[keyof Row]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)]'.
                  Type 'keyof Row' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
                    Type 'keyof Row' is not assignable to type 'Selected<Row>[keyof Row] extends string ? keyof Row : never'.
                      Type 'Selected<Row>[keyof Row]' is not assignable to type 'boolean'.
                        Type 'Selected<Row>[Row[keyof Row] extends string ? keyof Row : never]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)]'.
                          Type 'Row[keyof Row] extends string ? keyof Row : never' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
                            Type 'Row[keyof Row] extends string ? keyof Row : never' is not assignable to type 'Selected<Row>["selected"] extends string ? "selected" : never'.
                              Type 'Selected<Row>[Row[keyof Row] extends string ? keyof Row : never]' is not assignable to type 'boolean'.
                                Type 'Selected<Row>[{ [P in keyof Row]: Row[P] extends string ? P : never; }[keyof Row]]' is not assignable to type '{ selected: boolean; }[(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)]'.
                                  Type 'Row[keyof Row] extends string ? keyof Row : never' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
                                    Type 'keyof Row' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
                                      Type 'string | number | symbol' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
                                        Type 'string' is not assignable to type '(Selected<Row>[keyof Row] extends string ? keyof Row : never) & (Selected<Row>["selected"] extends string ? "selected" : never)'.
                                          Type 'string' is not assignable to type 'Selected<Row>[keyof Row] extends string ? keyof Row : never'.
                                            Type 'keyof Row' is not assignable to type 'Selected<Row>[keyof Row] extends string ? keyof Row : never'.
                                              Type 'string | number | symbol' is not assignable to type 'Selected<Row>[keyof Row] extends string ? keyof Row : never'.
                                                Type 'string' is not assignable to type 'Selected<Row>[keyof Row] extends string ? keyof Row : never'.
                                                  Type 'Row[keyof Row] extends string ? keyof Row : never' is not assignable to type 'Selected<Row>["selected"] extends string ? "selected" : never'.
                                                    Type 'Selected<Row>[{ [P in keyof Row]: Row[P] extends string ? P : never; }[keyof Row]]' is not assignable to type 'boolean'.
                                                      Type 'Selected<Row>[Row[keyof Row] extends string ? keyof Row : never]' is not assignable to type 'boolean'.
                                                        Type 'Selected<Row>[keyof Row]' is not assignable to type 'boolean'.
                                                          Type 'Row[string] | Row[number] | Row[symbol]' is not assignable to type 'boolean'.
                                                            Type 'Row[string]' is not assignable to type 'boolean'.

1 个答案:

答案 0 :(得分:2)

编译器不像您那样聪明,尤其是在推理依赖未解析的通用参数(例如NonSelected<Row>[P] extends string ? P : never)的条件类型时。如果您已经过了逻辑,并确定自己在做的事是安全的,那么明智的type assertion是必要的:

const getId = <Row>(row: Selected<Row>, key: Key<Row>) =>
  row[key] as unknown as ID<Selected<Row>>;

或者,您可以为编译器提供 可以使用的理由,例如,O类型的键索引的K类型的对象将产生一个值类型O[K]

const getId = <Row>(row: Selected<Row>, key: Key<Selected<Row>>): ID<Selected<Row>> =>
  row[key];

其中任何一个都应该使编译器满意,并且您的示例将继续按预期运行。

希望有帮助。祝你好运!