gpt4 book ai didi

需要所有元素的 typescript 类型保护

转载 作者:行者123 更新时间:2023-12-05 02:57:42 27 4
gpt4 key购买 nike

有没有办法用 typescript 要求数组元素,这样我就可以拥有

type E = keyof T; // Number of properties in T is unknown

在这个例子中让 T 被定义:

interface T{
el1:number,
el2:number,
el3:number
}

生成的类型保护应该检查 T 的所有属性,并且只有 T 的属性应该在结果数组中公开。例如使用上面的示例 T:

[{"arg":"el1"},{"arg":"el2"},{"arg":"el3"}]  //only correct option
[{"arg":"el1"},{"arg":"el2"}] // should fail
[{"arg":"el1"},{"arg":"el2"},{"arg":"el2"},{"arg":"el3"}] // should fail
[{"arg":"el1"},{"arg":"el2"},{"arg":"el8"}] // should fail

我用的钱

type ITestElements = {
fn: E
}[];

它只涵盖了第二个例子也是积极的。

最佳答案

我要定义这个:

type Arg<T> = T extends any ? { arg: T } : never;

这样我们就可以使用Arg<E> (相当于 {arg:"el1"}|{arg:"el2"}|{arg:"el3"} )如下。


您在这里所能期望的最好的是一些 generic辅助函数 verifyArray()这将强制执行其论点的限制:

  • 联合中的元素数组
  • 不遗漏联合中的任何元素
  • 并且不包含重复

而且会很丑。


没有可用具体类型可以为包含超过六个元素的联合强制执行此操作。可以使用一些非法递归或合法非递归但乏味的类型定义来采用像 0 | 1 | 2 | 3 这样的联合类型。并将其变成所有可能的联合 tuples符合您的标准。那会产生类似的东西

type AllTuples0123 = UnionToAllPossibleTuples<0 | 1 | 2 | 3>

相当于

type AllTuples0123 =
| [0, 1, 2, 3] | [0, 1, 3, 2] | [0, 2, 1, 3] | [0, 2, 3, 1] | [0, 3, 1, 2] | [0, 3, 2, 1]
| [1, 0, 2, 3] | [1, 0, 3, 2] | [1, 2, 0, 3] | [1, 2, 3, 0] | [1, 3, 0, 2] | [1, 3, 2, 0]
| [2, 0, 1, 3] | [2, 0, 3, 1] | [2, 1, 0, 3] | [2, 1, 3, 0] | [2, 3, 0, 1] | [2, 3, 1, 0]
| [3, 0, 1, 2] | [3, 0, 2, 1] | [3, 1, 0, 2] | [3, 1, 2, 0] | [3, 2, 0, 1] | [3, 2, 1, 0]

但是对于一个 n 元素的输入并集,它会产生一个 n! 的输出并集(即 n factorial )输出,它在 中增长得非常快名词。对于您的示例 "el1"|"el2"|"el3"没关系:

type AllPossibleTuplesOfArgs = UnionToAllPossibleTuples<Arg<E>>;
const okay: AllPossibleTuplesOfArgs =
[{ "arg": "el1" }, { "arg": "el2" }, { "arg": "el3" }]; // okay
const bad1: AllPossibleTuplesOfArgs = [{ "arg": "el1" }, { "arg": "el2" }]; // error!
const bad2: AllPossibleTuplesOfArgs = // error!
[{ "arg": "el1" }, { "arg": "el2" }, { "arg": "el2" }, { "arg": "el3" }];
const bad3: AllPossibleTuplesOfArgs =
[{ "arg": "el1" }, { "arg": "el2" }, { "arg": "el8" }] // error!

但我假设您想要的东西不会在您的对象中包含七个或更多属性时使您的编译器崩溃。所以让我们放弃UnionToAllPossibleTuples和任何具体类型。


那会怎样 verifyArray()看起来像?

首先让我们创建一个名为 NoRepeats<T> 的类型函数它采用元组类型 T并返回与 T 相同的内容当且仅当 T没有重复的元素...否则它返回一个修改过的元组 T不可分配。这将允许我们进行约束 T extends NoRepeats<T>说“元组类型 T 没有重复元素”。这是一种方法:

type NoRepeats<T extends readonly any[]> = { [M in keyof T]: { [N in keyof T]:
N extends M ? never : T[M] extends T[N] ? unknown : never
}[number] extends never ? T[M] : never }

所以 NoRepeats<[0,1,2]>[0,1,2] ,但是NoRepeats<[0,1,1]>[0,never,never] .那么verifyArray()可以这样写:

const verifyArray = <T>() => <U extends NoRepeats<U> & readonly T[]>(
u: (U | [never]) & ([T] extends [U[number]] ? unknown : never)
) => u;

它需要一个类型 T检查并返回一个新函数,确保其参数没有重复(来自 U extends NoRepeats<U> ),可分配给 T[] (来自 & readonly T ),并且不丢失 T 的任何元素(来自 & ([T] extends [U[number]] ? unknown : never))。是的,它很丑。让我们看看它是否有效:

const verifyArgEArray = verifyArray<Arg<E>>()
const okayGeneric = verifyArgEArray([{ "arg": "el3" }, { "arg": "el1" }, { "arg": "el2" }]); // okay
const bad1Generic = verifyArgEArray([{ "arg": "el1" }, { "arg": "el2" }]); // error
const bad2Generic = // error
verifyArgEArray([{ "arg": "el1" }, { "arg": "el2" }, { "arg": "el2" }, { "arg": "el3" }]);
const bad3Generic = // error
verifyArgEArray([{ "arg": "el1" }, { "arg": "el2" }, { "arg": "el8" }]);

这样就可以了。


这两者都迫使您与类型系统作斗争。您可以像 this answer 中那样创建构建器类它更适合类型系统,但涉及更多的运行时开销,并且可以说只是稍微不那么难看。


老实说,我建议尝试重构您的代码,而不是要求 TypeScript 强制执行此操作。最简单的方法是要求一个对象将这些值作为键(例如,只需创建一个类型为 T 或可能为 Record<keyof T, any> 的值)并使用它来代替(或在生成之前)数组。哦,希望这有帮助。祝你好运!

Playground link to code

关于需要所有元素的 typescript 类型保护,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59680489/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com