簡體   English   中英

打字稿:從對象創建接口或最窄的類型

[英]Typescript: Create an interface or the narrowest type from an Object

有沒有辦法從對象創建接口或窄類型?

例如,我想要類似的東西:

const obj : someType = {
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
};

type myType = narrowTypeOf obj

myType應該導致:

{
  a: string,
  b: string,
  c: {
    c: string
  }
};

可悲的是(或者更幸運的是) type mytype = typeof obj的構建會導致someType ,因為我確實已經聲明它是那種類型。

如果有一種方法可以使用對象作為類型定義的參數 - 那也可以解決我的問題。

除非SomeType特別是一個聯合,其中一個成員是您想要的窄類型,否則這是行不通的。 讓我們首先看看SomeType這樣一個聯合的快樂案例,即使顯然這不是您的用例:

type SomeType = {
  a: string,
  b: string,
  c: {
    c: string
  }
} | { foo: string } | { bar: string };

這里SomeType是你想要的類型和兩個不相關類型的聯合。 然后,當您將值分配給obj注釋為SomeType

const obj: SomeType = {
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
};

此時捕獲的typeof obj就是你想要的類型:

type MyType = typeof obj;
/* type MyType = {
    a: string;
    b: string;
    c: {
        c: string;
    };
} */

MyType類型不包含{foo: string}{bar: string} 這是因為編譯器在分配時使用基於控制流的類型分析來縮小聯合類型(請參閱microsoft/TypeScript#8010以了解實現這一點的 PR 說明)。

但是在分配時沒有基於控制流的非聯合類型變量的縮小。 有一次 TS 團隊似乎正在考慮它,並且有一個開放的建議(參見microsoft/TypeScript#16976 )用於縮小非聯合類型的控制流,但現在它不是語言的一部分。

這意味着如果您的SomeType只是比您想要的更寬,但不是特別包含該元素的聯合,您就會陷入困境。 一旦將變量注釋為該寬類型,對該變量的所有賦值都將忘記有關該類型當前值的任何更具體的信息。


這里正確的做法是不要注釋變量的類型。 您應該將變量注釋為比您分配的值的類型更寬的類型的唯一原因是,如果您想稍后改變變量的內容以使其成為該更寬類型的其他居民。 例如,如果SomeType是這樣給出的:

interface SomeType {
  a: string | number;
  b: unknown;
  c: object;
}

那么您將以下聲明注釋為SomeType的唯一充分理由:

const obj: SomeType = {
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
};

如果您打算稍后執行此操作,將會是:

obj.a = 123;
obj.b = () => 456;
obj.c = [789];

假設您不打算需要更寬的類型,那么您應該讓編譯器推斷obj的類型。 如果你擔心這會讓你分配一些與SomeType不兼容SomeType ,那么你可以使用一個輔助函數,它的作用有點像縮小注釋:

const annotateAs = <T>() => <U extends T>(u: U) => u;

這個輔助函數可以這樣使用:

const asSomeType = annotateAs<SomeType>();

現在asSomeType()是一個函數,它將在不加寬的情況下返回其輸入,但如果該輸入無法分配給SomeType ,則會出現錯誤:

const oops = asSomeType({
  a: "a",
  b: "b",
  c: "c" // error! string is not an object
})

現在你可以像這樣編寫obj賦值:

const obj = asSomeType({
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
});

成功了,所以obj肯定是SomeType ,但現在obj的類型是:

type MyType = typeof obj;
/* type MyType = {
    a: string;
    b: string;
    c: {
        c: string;
    };
} */

如你所願。


好的,希望有幫助; 祝你好運!

Playground 鏈接到代碼

暫無
暫無

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

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