扩展排他联合的打字稿通用参数
Posted
技术标签:
【中文标题】扩展排他联合的打字稿通用参数【英文标题】:Typescript generic parameters that extends exclusive unions 【发布时间】:2019-03-26 08:09:18 【问题描述】:在 TypeScript 中,有没有办法定义一个只扩展联合类型的一个值的泛型参数?
例如,假设我声明一个联合类型如下:
type Any = "A" | "B"
那么如果我在函数定义中使用如下类型:
const fn = <T extends Any>(arg: T[]) =>
那么 args 参数可以是一个包含“A”和“B”值的数组;例如,这将是有效的:
let x = fn(["A", "B"])
这违背了在函数定义中使用泛型参数的目的(即,将 args 参数数组中的值限制为仅一种特定类型)
当然,我可以这样定义函数:
const fn = (arg: "A"[] | "B"[]) =>
但如果联合中的组件类型数量很大,这可能不切实际
【问题讨论】:
所以要禁止fn(null as ("A" | "B")[])
?
@TitianCernicova-Dragomir 不,我想禁止let x = fn(["A", "B"])
是的 .. 就推断的T
而言,这是一回事 :)
@TitianCernicova-Dragomir:是的,实际上是一样的,我的错
【参考方案1】:
有一个提议的功能可以让您告诉编译器T
是多种类型之一,而不是它们的联合。 issue 被标记为在讨论中,所以可以为它加一个 +1。
与此同时,如果T
是使用条件类型的联合,我们可以强制编译器给我们一个错误:
type Any = "A" | "B"
type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
type NoUnion<T, TError> = [T] extends [UnionToIntersection<T>] ? : TError
type d = NoUnion<Any, "">
const fn = <T extends Any>(arg: T[] & NoUnion<T, "Must be A or B not a union">) =>
fn(null as "A"[])
fn(null as "B"[])
fn(null as ("A" | "B")[]) //error Type 'Any[]' is not assignable to type '"Must be A or B not a union"'
【讨论】:
好的@Titian,但是当您将实际参数传递给 fn(例如,fn(["A", "B"])
)时,类型错误不会给出正确的消息。我得到的错误是:Type 'string' is notassignable to type 'Any'。无论如何,您的解决方案可以简单地简化为const fn = <T extends Any>(arg: T extends (infer X)? X[] : never) =>
@prmph 是的,这也是一个不错的选择 :) 但是你会注意到 T
并不是真正的类型,它始终是 "A" | B"
【参考方案2】:
这是一个解决方案,非常简单。我还通过添加返回类型来增加它的趣味性 - 结束它。
type Any = "A" | "B" | "C" | "D" | "E"
type Fn<T> = (arg:T[])=>T
// This distributive conditional operator maps the
// union of elements-of-Any to a union-of-functions with exclusive Parameters
type FnAny_<T> = T extends any ? Fn<T> : never;
type FnAny = FnAny_<Any>
// Obviously you have to define the real content of `fnAny`
// to correctly match the definition - this is a dummy defn
function fnAny<T extends FnAny>(...a:Parameters<T>):ReturnType<T>
if (Math.random()<0.5)
return undefined as unknown as ReturnType<T>
else
return undefined as unknown as ReturnType<T>
fnAny(["A","A"]) // ok
fnAny(["A","E"]) // ERROR - rejected by type checker
此解决方案适用于所有可用版本的 Typescript Playground 从 3.3.3333 到最新的 4.5.4。
错误信息:
'[("A" | "E")[]]' 类型的参数不能分配给'[arg: "A"[]] | 类型的参数[参数:“B”[]] | [参数:“C”[]] | [参数:“D”[]] | [arg:“E”[]]'。 类型 '[("A" | "E")[]]' 不能分配给类型 '[arg: "E"[]]'。 类型 '("A" | "E")[]' 不能分配给类型 '"E"[]'。 类型“A”| “E”不能分配给类型“E”。 类型“A”不能分配给类型“E”。【讨论】:
以上是关于扩展排他联合的打字稿通用参数的主要内容,如果未能解决你的问题,请参考以下文章