gpt4 book ai didi

typescript - 当 noUncheckedIndexedAccess 为 true 时,按长度缩小数组类型的类型安全方法

转载 作者:行者123 更新时间:2023-12-04 12:53:26 27 4
gpt4 key购买 nike

给定一个接受单个 string[] 的函数参数myArray .
如果我评估 .length属性并且该属性大于 0,那么(假设我的变量不是 any 变相)它不可能用于 myArray[0]未定义。但是,如果我启用 noUncheckedIndexedAccess ,其类型将为 string | undefined

const myFunc = (myArray: string[]) => {
if(myArray.length > 0) {
const foo = myArray[0] // now typed at 'string | undefined'
}
}
现在我可以更改 if 语句来评估 myArray[0] , 和 undefined如您所料,已从类型中删除。但是,如果我现在想检查数组的长度是否大于 6,该怎么办。我不想对索引 0-5 执行相同的操作以正确缩小类型。例如:
const myFunc = (myArray: string[]) => {
if(myArray[0] && myArray[1] && myArray[2] && myArray[3] && myArray[4] && myArray[5]) {
const foo = myArray[0] // now typed at 'string', but this is uggggly
}
}
有没有更优雅的方法来根据数组的长度缩小类型,还是我将不得不考虑为 TypeScript 代码库做出贡献?

最佳答案

正如您所怀疑的那样,TypeScript 不会根据对其 length 的检查自动缩小数组的类型。属性(property)。之前已在 microsoft/TypeScript#38000 中提出了这一建议。 ,标记为“太复杂”。看起来之前有人建议过,在 microsoft/TypeScript#28837 ,仍处于打开状态并标记为“等待更多反馈”。也许你可以去那个问题并留下反馈,说明为什么这样的事情对你有帮助,为什么当前的解决方案不够充分,但我不知道它会产生多大影响。无论哪种方式,我都怀疑 TS 团队现在是否正在接受拉取请求来实现这样的功能。
如果没有任何自动缩小,你可以写一个 user defined type guard function这有你想要的效果。这是一种可能的实现:

type Indices<L extends number, T extends number[] = []> =
T['length'] extends L ? T[number] : Indices<L, [T['length'], ...T]>;

type LengthAtLeast<T extends readonly any[], L extends number> =
Pick<Required<T>, Indices<L>>

function hasLengthAtLeast<T extends readonly any[], L extends number>(
arr: T, len: L
): arr is T & LengthAtLeast<T, L> {
return arr.length >= len;
}
Indices<L> type 旨在采用单个的、相对较小的、非负的整数数字 literal type L并返回 union长度为 L 的数组的数字索引.另一种说法是 Indices<L>应该是小于 L 的非负整数的并集.观察:
type ZeroToNine = Indices<10>
// type ZeroToNine = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
这种类型的工作原理是利用 recursive conditional types连同 variadic tuple types步行从 0高达 L .递归类型往往能正常工作,直到它们不能正常工作,这里的一个重要警告是,如果您传入 L,事情会很奇怪,甚至会引发错误。太大,或小数,或负数,或 number ,或工会。这是这种方法的一个重要警告。
接下来, LengthAtLeast<T, L>采用数组类型 T和长度 L ,并返回一个对象,该对象已知在长度至少为 L 的数组的所有索引处都具有属性。 .像这样:
type Test = LengthAtLeast<["a"?, "b"?, "c"?, "d"?, "e"?], 3>
/* type Test = {
0: "a";
1: "b";
2: "c";
} */

type Test2 = LengthAtLeast<string[], 2>
/* type Test2 = {
0: string;
1: string;
} */
最后, hasLengthAtLeast(arr, len)是类型保护函数。如果返回 true ,然后 arr从类型 T 缩小至 T & LengthAtLeast<T, L> .让我们看看它的实际效果:
const myFunc = (myArray: string[]) => {
if (hasLengthAtLeast(myArray, 6)) {
myArray[0].toUpperCase(); // okay
myArray[5].toUpperCase(); // okay
myArray[6].toUpperCase(); // error, possibly undefined
}
}
看起来挺好的。编译器很高兴允许您处理 myArray[0]myArray[5]如定义,但 myArray[6]仍然可能未定义。

无论如何,如果您决定使用类型保护,您可能需要在复杂性与需要使用它的程度之间取得平衡。如果你只检查几个地方的长度,使用 non- null assertion operator 可能是值得的。喜欢 myArray[0]!.toUpperCase()而不必担心让编译器为您验证类型安全。
或者,如果您无法控制 len 的值,那么您可能不想要脆弱的递归条件类型,而是构建更健壮但不那么灵活的东西(例如可能只适用于特定的 len 值,如 in a comment on microsoft/TypeScript#38000 的重载类型保护)。
这一切都取决于您的用例。
Playground link to code

关于typescript - 当 noUncheckedIndexedAccess 为 true 时,按长度缩小数组类型的类型安全方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69368851/

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