强制数组在联合类型上是详尽的 [英] Enforce that an array is exhaustive over a union type

查看:22
本文介绍了强制数组在联合类型上是详尽的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定一个使用此处描述的技术创建的强类型元组:

const tuple = <T extends string[]>(...args: T) => args;
const furniture = tuple('chair', 'table', 'lamp');

// typeof furniture[number] === 'chair' | 'table' | 'lamp'

我想在设计时断言它对另一种联合类型是详尽无遗的:

I want to assert at design time that it's exhaustive over another union type:

type Furniture = 'chair' | 'table' | 'lamp' | 'ottoman'

如何创建一个类型以确保 furniture 包含每个且仅包含 Furniture 联合中的类型?

How can I create a type that will ensure that furniture contains each and only the types in the Furniture union?

目标是能够在设计时像这样创建一个数组,并且如果Furniture发生变化,它就会失败;理想的语法可能如下所示:

The goal is to be able to create an array at design time like this, and have it fail should Furniture change; an ideal syntax might look like:

const furniture = tuple<Furniture>('chair', 'table', 'lamp')

推荐答案

TypeScript 并没有真正直接支持穷举数组".您可以引导编译器进行检查,但这对您来说可能有点麻烦.一个绊脚石是缺少部分类型参数推断(如中的要求)微软/TypeScript#26242).这是我的解决方案:

TypeScript doesn't really have direct support for an "exhaustive array". You can guide the compiler into checking this, but it might be a bit messy for you. A stumbling block is the absence of partial type parameter inference (as requested in microsoft/TypeScript#26242). Here is my solution:

type Furniture = 'chair' | 'table' | 'lamp' | 'ottoman';

type AtLeastOne<T> = [T, ...T[]];

const exhaustiveStringTuple = <T extends string>() =>
  <L extends AtLeastOne<T>>(
    ...x: L extends any ? (
      Exclude<T, L[number]> extends never ? 
      L : 
      Exclude<T, L[number]>[]
    ) : never
  ) => x;


const missingFurniture = exhaustiveStringTuple<Furniture>()('chair', 'table', 'lamp');
// error, Argument of type '"chair"' is not assignable to parameter of type '"ottoman"'

const extraFurniture = exhaustiveStringTuple<Furniture>()(
  'chair', 'table', 'lamp', 'ottoman', 'bidet');
// error, "bidet" is not assignable to a parameter of type 'Furniture'

const furniture = exhaustiveStringTuple<Furniture>()('chair', 'table', 'lamp', 'ottoman');
// okay

如您所见,exhaustiveStringTuple 是一个 curred 函数,其唯一目的是采用手动指定的类型参数 T,然后返回一个新函数,该函数采用类型受 T 约束但由调用推断的参数.(如果我们有适当的部分类型参数推断,可以消除柯里化.)在您的情况下,T 将被指定为 Furniture.如果您只关心 exhaustiveStringTuple(),那么您可以改用它:

As you can see, exhaustiveStringTuple is a curried function, whose sole purpose is to take a manually specified type parameter T and then return a new function which takes arguments whose types are constrained by T but inferred by the call. (The currying could be eliminated if we had proper partial type parameter inference.) In your case, T will be specified as Furniture. If all you care about is exhaustiveStringTuple<Furniture>(), then you can use that instead:

const furnitureTuple =
  <L extends AtLeastOne<Furniture>>(
    ...x: L extends any ? (
      Exclude<Furniture, L[number]> extends never ? L : Exclude<Furniture, L[number]>[]
    ) : never
  ) => x;

游乐场链路编码

这篇关于强制数组在联合类型上是详尽的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆