扩展排他联合的打字稿通用参数

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 = &lt;T extends Any&gt;(arg: T extends (infer X)? X[] : never) =&gt; @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”。

【讨论】:

以上是关于扩展排他联合的打字稿通用参数的主要内容,如果未能解决你的问题,请参考以下文章

打字稿通用和扩展

通用无状态组件 React 的类型?或在打字稿中扩展通用函数接口以具有进一步的通用性?

打字稿推断传递了通用参数

接受通用参数和字符串数组的打字稿函数

打字稿通用承诺返回类型

打字稿泛型类参数