![](/img/trans.png)
[英]Typescript: Is it possible to evaluate generic type into string literal?
[英]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!
看起來不錯!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.