簡體   English   中英

TypeScript 數據映射器 function 參數推斷從不

[英]TypeScript data mapper function argument infers never

考慮以下示例:

type columns = {
    A: number;
    B: string;
    C: boolean;
};
const mapper: { [T in keyof columns]: (item: columns[T]) => string } = {
    A: item => `${item}`,
    B: item => item,
    C: item => (item ? "Good" : "Bad"),
};
const data: columns[] = [
    { A: 0, B: "Hello", C: true },
    { A: 1, B: "World", C: false },
];
const keys: (keyof columns)[] = ["A", "B", "C"];
data.map(item => keys.map(key => mapper[key](item[key] as never)));
// should return [["0", "Hello", "Good"], ["1", "World", "Bad"]]

在最后一行, key的類型為keyof columns ,即"A" | "B" | "C" "A" | "B" | "C" "A" | "B" | "C" ,這使得mapper[key]減少到(item: number & string & boolean) => string ,即(item: never) => string (指出我的概念是否錯誤。)

所以問題是,我怎樣才能重寫代碼使得item[key]不需要強制轉換為never

這是 TypeScript 和microsoft/TypeScript#30581主題的一般限制。 編譯器確實無法查看像mapper[key](item[key])這樣的單個表達式並使用控制流分析來分析它以查看它是否安全。

問題是mapper[key]item[key]都是union types mapper[key]的類型是((item: number) => string) | ((item: string) => string) | ((item: boolean)=>string) ((item: number) => string) | ((item: string) => string) | ((item: boolean)=>string) ((item: number) => string) | ((item: string) => string) | ((item: boolean)=>string)並且item[key]的類型是number | string | boolean number | string | boolean number | string | boolean 但是編譯器沒有很好的方法來跟蹤這些值的類型之間的相關性 它將所有工會視為基本上彼此獨立。 我們知道mapper[key](item: number) => stringitem[key]number時,但是編譯器沒有。 據它所知, mapper[key]可以接受一個number ,而item[key]string 這種相關性與我們在兩個表達式中使用相同的key這一事實有關,但編譯器只跟蹤key的類型,而不是它的標識。

當你獨立對待mapper[key]item[key]時,你有點卡住了。 您只能調用 function 類型與 arguments 的聯合,這將適用於聯合的每個成員 也就是說,參數的交集......所以編譯器將mapper[key]視為可分配給(item: number & string & boolean) => string ,又名(item: never) => string ... 意思是這樣的function 通常不安全。

除非有某種方法可以告訴編譯器跟蹤聯合類型表達式之間的相關性,否則沒有很好的方法可以繼續。 如果您最關心類型安全,則可以編寫一些冗余代碼來獲得它:

data.map(item => keys.map(key =>
  key === "A" ? mapper[key](item[key]) :
    key === "B" ? mapper[key](item[key]) :
      mapper[key](item[key])
)); // no error but it's redundant and repetitive 
// and also redundant

如果您關心方便而不是類型安全,那么您可以使用類型斷言來抑制錯誤。 您的item[key] as never示例是一種方法,盡管您在技術上對item[key]是什么撒謊。 如果你不想撒謊,你可以像這樣使用通用回調 function :

data.map(item => keys.map(<K extends keyof Columns>(key: K) =>
  (mapper[key] as (item: Columns[K]) => string)(item[key]) // okay
));

您必須斷言mapper[key]是類型的值(item: Columns[K]) => string)因為編譯器無法驗證這一點,即使理論上它應該能夠驗證。 當您嘗試調用它時,它會急切地將mapper[key]解析為函數的聯合。 而且由於mapper[key]確實是那種類型的值,我們沒有撒謊。 這里缺乏類型安全是因為如果有人邪惡地切換了mapper的條目,編譯器不會注意到:

const evilMapper = {
  A: mapper.B,
  B: mapper.C,
  C: mapper.A
}

data.map(item => keys.map(<K extends keyof Columns>(key: K) =>
  (evilMapper[key] as (item: Columns[K]) => string)(item[key]) // okay?!
));

而冗余冗余的版本會開始尖叫:

data.map(item => keys.map(key =>
  key === "A" ? evilMapper[key](item[key]) : // error
    key === "B" ? evilMapper[key](item[key]) : // error
      evilMapper[key](item[key]) // error
)); 

Playground 代碼鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM