TypeScript keyof 索引类型太宽
TypeScript keyof index type is too wide
抱歉,我确定这个问题已经在某个地方得到了回答,但我不确定 google。如果标题中的术语有误,请编辑我的问题。
我有这样的东西:
type RowData = Record<string, unknown> & {id: string};
type Column<T extends RowData, K extends keyof T> = {
key: K;
action: (value: T[K], rowData: T) => void;
}
type RowsAndColumns<T extends RowData> = {
rows: Array<T>;
columns: Array<Column<T, keyof T>>;
}
并且 TypeScript 应该能够推断出 action
函数的类型,通过检查行的形状,以及为列赋予的 key
值:
即:
function myFn<T extends RowData>(config: RowsAndColumns<T>) {
}
myFn({
rows: [
{id: "foo",
bar: "bar",
bing: "bing",
bong: {
a: 99,
b: "aaa"
}
}
],
columns: [
{
key: "bar",
action: (value, rowData) => {
console.log(value);
}
},
{
key: "bong",
action: (value, rowData) => {
console.log(value.a); //Property 'a' does not exist on type 'string | { a: number; b: string; }'.
}
}
]
});
问题是,TypeScript 似乎将值的类型 (value: T[K]
) 推导为 'the types of all accessible by all keys of T' 而不是仅使用 object.[=17 列中提供的键=]
为什么 TypeScript 会这样,我该如何解决?
好的答案是定义一些特定的术语和概念。
我想我想把我的 K extends keyof T
改成 'K is a keyof T, but only one key, and it never changes'。
如果您希望 T
的键像 "bong" | "bing" | ...
一样是 union of literals(而不仅仅是 string
),那么您可以表达一个本身就是类型的类型keyof T
.
中每个键 K
的 Column<T, K>
的 union
我通常会立即 indexing (lookup) into a mapped type:
type SomeColumn<T extends RowData> = {
[K in keyof T]-?: Column<T, K>
}[keyof T]
但您也可以通过 distributive conditional types:
type SomeColumn<T extends RowData> = keyof T extends infer K ?
K extends keyof T ? Column<T, K> : never : never;
无论哪种方式,您的 RowsAndColumns
类型将使用 SomeColumn
而不是 Column
:
type RowsAndColumns<T extends RowData> = {
rows: Array<T>;
columns: Array<SomeColumn<T>>;
}
这使您所需的用例按预期工作而没有编译错误:
myFn({
rows: [
{
id: "foo",
bar: "bar",
bing: "bing",
bong: {
a: 99,
b: "aaa"
}
}
],
columns: [
{
key: "bar",
action: (value, rowData) => {
console.log(value);
}
},
{
key: "bong",
action: (value, rowData) => {
console.log(value.a);
}
},
]
});
抱歉,我确定这个问题已经在某个地方得到了回答,但我不确定 google。如果标题中的术语有误,请编辑我的问题。
我有这样的东西:
type RowData = Record<string, unknown> & {id: string};
type Column<T extends RowData, K extends keyof T> = {
key: K;
action: (value: T[K], rowData: T) => void;
}
type RowsAndColumns<T extends RowData> = {
rows: Array<T>;
columns: Array<Column<T, keyof T>>;
}
并且 TypeScript 应该能够推断出 action
函数的类型,通过检查行的形状,以及为列赋予的 key
值:
即:
function myFn<T extends RowData>(config: RowsAndColumns<T>) {
}
myFn({
rows: [
{id: "foo",
bar: "bar",
bing: "bing",
bong: {
a: 99,
b: "aaa"
}
}
],
columns: [
{
key: "bar",
action: (value, rowData) => {
console.log(value);
}
},
{
key: "bong",
action: (value, rowData) => {
console.log(value.a); //Property 'a' does not exist on type 'string | { a: number; b: string; }'.
}
}
]
});
问题是,TypeScript 似乎将值的类型 (value: T[K]
) 推导为 'the types of all accessible by all keys of T' 而不是仅使用 object.[=17 列中提供的键=]
为什么 TypeScript 会这样,我该如何解决?
好的答案是定义一些特定的术语和概念。
我想我想把我的 K extends keyof T
改成 'K is a keyof T, but only one key, and it never changes'。
如果您希望 T
的键像 "bong" | "bing" | ...
一样是 union of literals(而不仅仅是 string
),那么您可以表达一个本身就是类型的类型keyof T
.
K
的 Column<T, K>
的 union
我通常会立即 indexing (lookup) into a mapped type:
type SomeColumn<T extends RowData> = {
[K in keyof T]-?: Column<T, K>
}[keyof T]
但您也可以通过 distributive conditional types:
type SomeColumn<T extends RowData> = keyof T extends infer K ?
K extends keyof T ? Column<T, K> : never : never;
无论哪种方式,您的 RowsAndColumns
类型将使用 SomeColumn
而不是 Column
:
type RowsAndColumns<T extends RowData> = {
rows: Array<T>;
columns: Array<SomeColumn<T>>;
}
这使您所需的用例按预期工作而没有编译错误:
myFn({
rows: [
{
id: "foo",
bar: "bar",
bing: "bing",
bong: {
a: 99,
b: "aaa"
}
}
],
columns: [
{
key: "bar",
action: (value, rowData) => {
console.log(value);
}
},
{
key: "bong",
action: (value, rowData) => {
console.log(value.a);
}
},
]
});