5

抱歉,我确信这已经在某个地方得到了回答,但我不确定要谷歌什么。如果标题中的术语有误,请编辑我的问题。

我有这样的事情:

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赋予列的值来推断函数的类型:

IE:

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 ( value: T[K]) 的类型导出为“T 的所有键可访问的所有类型”,而不是仅使用列对象中提供的键。

为什么 TypeScript 会这样做,我该如何解决?

做出一些好的答案是定义一些特定的术语和概念。

我想我想将我的更改K extends keyof T为“K 是 T 的一个键,但只有一个键,而且它永远不会改变”。

4

1 回答 1

6

如果您希望键 ofT是像(而不仅仅是)这样的文字的联合,那么您可以表达一个类型,该类型本身就是中每个键的并"bong" | "bing" | ...stringColumn<T, K>Kkeyof T

我通常通过立即索引(查找)映射类型来做到这一点:

type SomeColumn<T extends RowData> = {
  [K in keyof T]-?: Column<T, K>
}[keyof T]

但您也可以通过分布式条件类型来做到这一点:

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);
      }
    },
  ]
});

Playground 代码链接

于 2020-11-09T14:21:25.673 回答