我有以下类型定义。
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]
应该总是可以正常工作,因为即使row
是Selected<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'.
答案 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];
其中任何一个都应该使编译器满意,并且您的示例将继续按预期运行。
希望有帮助。祝你好运!