簡體   English   中英

打字稿無法從通用接口屬性推斷出正確的類型

[英]Typescript cannot infer correct type from generic interface property

我有一些場景,我有一個使用字符串、數字和布爾值的對象,以及一個返回正確類型值的 getter,例如這樣的東西:

interface AllowedMapTypings {
    'str': string;
    'lon': number;
    'str2': string;
}

const obj: AllowedMapTypings = {
    'str': 'foo',
    'lon': 123,
    'str2': 'foo'
};

function foo<T extends keyof AllowedMapTypings>(key: T): AllowedMapTypings[T] {
    return obj[key];
}

let str = foo('str'); // correctly inferred type 'string'

但是,如果我使用推斷的接口類型作為參數,則不起作用:

function fn<T extends keyof AllowedMapTypings>(key: string, kind: T, value: AllowedMapTypings[T]) {
    if (kind === 'str') {
        console.log(value.length); // Property 'length' does not exist on type 'AllowedMapTypings[T]'.
    }
}

看起來條件kind === 'str'不能正確地作為類型保護。 我是否遺漏了什么,或者這是 TS 中缺少的功能/錯誤?

這是一個已知限制,請參閱microsoft/TypeScript#13995microsoft/TypeScript#24085 當您執行if (kind === 'str') {}時發生的那種控制流縮小不會作用於泛型類型參數或此類類型的值。 我認為有人可能會爭辯說if (kind === 'str') {}應該將kindT縮小到T & 'str' ,但即使編譯器為您這樣做,它也不會縮小value的類型. 即使您知道kind的類型與value的類型相關,編譯器也不會。

你總是可以通過自由使用類型斷言來解決這個問題。 如果您想要更多的類型安全性,您可以使用一種變通方法,將泛型類型的值擴展到具體聯合,並將相關值打包到一個可以按您期望的方式縮小范圍的單個變量中。 例如:

type KindValuePair<K extends keyof AllowedMapTypings = keyof AllowedMapTypings> =
  K extends any ? [K, AllowedMapTypings[K]] : never;

KindValuePair類型擴展為["str", string] | ["lon", number] | ["str2", string] ["str", string] | ["lon", number] | ["str2", string] ["str", string] | ["lon", number] | ["str2", string] ,這是您實際希望作為kindvalue允許的事物的聯合。 (我可以手動將KindValuePair設置KindValuePair聯合,但我使用分布式條件類型讓編譯器為我計算出來。)

然后你可以這樣做:

function fn<T extends keyof AllowedMapTypings>(key: string, kind: T, value: AllowedMapTypings[T]) {
  const kindValuePair = [kind, value] as KindValuePair; // assertion here
  if (kindValuePair[0] === 'str') {
    console.log(kindValuePair[1].length); // okay
  }
}

您斷言[kind, value]KindValuePair類型,然后您可以在kindValuePair上使用控制流縮小來維持您在檢查'str'后希望在其元素之間看到的關系。 如果這對你有用,你甚至可以使用rest 參數使函數具體而不是通用的:

function fn(key: string, ...kindValuePair: KindValuePair) {
  if (kindValuePair[0] === 'str') {
    console.log(kindValuePair[1].length); // okay
  }
}

這完全避免了斷言,並且就像我想象的那樣類型安全。 它還具有禁止這樣的調用的副作用:

fn("", Math.random() < 0.5 ? 'str' : 'lon', 1); // error

這在通用版本中是允許的(它將T指定為'str' | 'lon' )。

暫無
暫無

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

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