簡體   English   中英

將通用打字稿類型限制為單個字符串文字值,不允許聯合

[英]Restrict generic typescript type to single string literal value, disallowing unions

我有一個泛型實體類型,泛型用於基於一組字符串文字定義字段類型:

type EntityTypes = 'foo' | 'bar' | 'baz';

type EntityMappings = {
  foo: string;
  bar: number;
  baz: Array<string>;
}

type GenericEntity<T extends EntityTypes> = {
  type: T;
  fieldProperty: EntityMappings[T];
}

我想要做的是要求 GenericEntity 的所有實例都有一個type字段(字符串文字),然后定義 fieldProperty 的類型,例如:

const instance: GenericEntity<'foo'> = {
  type: 'foo',
  fieldProperty: 'hello',
};

const otherInstance: GenericEntity<'baz'> = {
  type: 'baz',
  fieldProperty: ['a', 'b', 'c'],
}

但是,因為T extends EntityTypes允許在 EntityTypes 中合並多個字符串文字值,所以我能夠做到這一點,但我想禁止:

const badInstance: GenericEntity<'foo' | 'baz'> = {
  type: 'baz',
  fieldProperty: 'blah',
};

編譯是因為現在type'foo' | 'baz' 'foo' | 'baz'和 fieldProperty 是string | Array<string> string | Array<string> ,但是這兩個字段不再像我想要的那樣對應。

有沒有辦法進一步限制 GenericEntity 上的泛型聲明,只允許一個唯一的字符串文字值? 除此之外,還有其他方法可以堅持 GenericEntity 的任何實例都具有對應的type字段和fieldProperty字段嗎?

目前沒有直接的方法將泛型類型參數限制為union的單個成員。 microsoft/TypeScript#27808有一個開放的功能請求,以支持T extends oneof EntityTyes之類的東西,但這還沒有實現。 如果您想看到它發生,您可以訪問該問題並給它一個👍,但我不知道它會產生多大影響。

這意味着T extends EntityTypes可以允許T成為EntityTypes的任何子類型,包括完整的EntityTypes聯合。 在實踐中,這往往不是什么大問題,因為通常這樣的T確實被推斷為單個成員(人們經常調用foo("x")foo("y")而不是foo(Math.random()<0.5?"x":"y") )。 但有時它會導致問題,尤其是像你這樣的示例代碼。


那么我們該如何解決這個問題呢? 鑒於您的特定示例代碼,我想說您希望GenericEntity實際上更像是具有三個成員的可區分聯合,而不是泛型類型。 但是您可以通過microsoft/TypeScript#47109中創造的分布式對象類型來獲得兩者。 它看起來像這樣:

type GenericEntity<T extends EntityTypes = EntityTypes> = { [U in T]: {
  type: U;
  fieldProperty: EntityMappings[U];
} }[T]

我們采用傳入的類型T映射其成員,然后使用T 對其進行索引 如果T是單個字符串文字,這沒有實際效果,但是當它是一個聯合時,結果也是一個沒有任何不良“交叉相關”術語的聯合:

type GE = GenericEntity;
/* type GE = {
    type: "foo";
    fieldProperty: string;
} | {
    type: "bar";
    fieldProperty: number;
} | {
    type: "baz";
    fieldProperty: string[];
} */

(我還為T設置了一個通用參數默認值,所以沒有類型參數的GenericEntity是我們在這里真正想要的完整聯合。)

所以我們正在做的是:我們不是在T禁止聯合,而是通過分配它們來處理它們。

現在事情會如你所願:

const instance: GenericEntity<'foo'> = {
  type: 'foo',
  fieldProperty: 'hello',
} // okay;

const otherInstance: GenericEntity<'baz'> = {
  type: 'baz',
  fieldProperty: ['a', 'b', 'c'],
} // okay

const badInstance: GenericEntity<'foo' | 'baz'> = {
  type: 'baz',
  fieldProperty: 'blah',
}; // error!

看起來不錯!

Playground 代碼鏈接

暫無
暫無

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

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