TypeScript const 断言:如何使用 Array.prototype.includes?
Posted
技术标签:
【中文标题】TypeScript const 断言:如何使用 Array.prototype.includes?【英文标题】:TypeScript const assertions: how to use Array.prototype.includes? 【发布时间】:2019-10-27 03:56:41 【问题描述】:我正在尝试使用元素数组作为联合类型,这在 TS 3.4 中使用 const 断言变得很容易,所以我可以这样做:
const CAPITAL_LETTERS = ['A', 'B', 'C', ..., 'Z'] as const;
type CapitalLetter = typeof CAPITAL_LETTERS[string];
现在我想测试一个字符串是否为大写字母,但以下失败并显示“不可分配给类型的参数”:
let str: string;
...
CAPITAL_LETTERS.includes(str);
有没有比将CAPITAL_LETTERS
转换为unknown
然后再转换为Array<string>
更好的方法来解决这个问题?
【问题讨论】:
我想你想要type CapitalLetter = typeof CAPITAL_LETTERS[number]
...也就是说,索引number
,而不是string
。
【参考方案1】:
使用 lodash
const CAPITAL_LETTERS = ['A', 'B', 'C', 'Z'] as const;
_.includes(CAPITAL_LETTERS, 'A');
【讨论】:
虽然这段代码 sn-p 可以解决问题,including an explanation 将帮助人们理解你的代码建议的原因。【参考方案2】:这是一个适用于字符串和字符串文字的解决方案,使用 TypeScript 4.1 Template Literal Types 不会破坏其他任何内容,并且在条件下使用时为了方便起见还缩小了类型:
declare global
interface ReadonlyArray<T>
includes<S, R extends `$Extract<S, string>`>(
this: ReadonlyArray<R>,
searchElement: S,
fromIndex?: number
): searchElement is R & S;
最初由 noppa 在a TypeScript github issue related to this 发布。
【讨论】:
【参考方案3】:另一种解决方法是使用类型保护
https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards
const myConstArray = ["foo", "bar", "baz"] as const
function myFunc(x: string)
//Argument of type 'string' is not assignable to parameter of type '"foo" | "bar" | "baz"'.
if (myConstArray.includes(x))
//Hey, a string could totally be one of those values! What gives, TS?
//get the string union type
type TMyConstArrayValue = typeof myConstArray[number]
//Make a type guard
//Here the "x is TMyConstArrayValue" tells TS that if this fn returns true then x is of that type
function isInMyConstArray(x: string): x is TMyConstArrayValue
return myConstArray.includes(x as TMyConstArrayValue)
//Note the cast here, we're doing something TS things is unsafe but being explicit about it
//I like to this of type guards as saying to TS:
//"I promise that if this fn returns true then the variable is of the following type"
function myFunc2(x: string)
if (isInMyConstArray(x))
//x is now "foo" | "bar" | "baz" as originally intended!
虽然您必须引入另一个“不必要的”功能,但最终看起来干净且工作完美。在你的情况下,你会添加
const CAPITAL_LETTERS = ['A', 'B', 'C', ..., 'Z'] as const;
type CapitalLetter = typeof CAPITAL_LETTERS[string];
function isCapitalLetter(x: string): x is CapitalLetter
return CAPITAL_LETTERS.includes(x as CapitalLetter)
let str: string;
isCapitalLetter(str) //Now you have your comparison
//Not any more verbose than writing .includes inline
if(isCapitalLetter(str))
//now str is of type CapitalLetter
【讨论】:
这很酷!我不知道类型保护功能。【参考方案4】:The standard library signature for Array<T>.includes(u)
假定要检查的值与数组元素T
的类型相同或更窄。但在您的情况下,您正在做相反的事情,检查一个 wider 类型的值。事实上,只有在T
和U
之间没有重叠时(即T & U
是never
),你才会说Array<T>.includes<U>(x: U)
是一个错误并且必须被禁止。
现在,如果您不打算经常对includes()
进行这种“相反”的使用,并且希望运行时效应为零,那么您应该通过类型断言将CAPITAL_LETTERS
扩大到ReadonlyArray<string>
:
(CAPITAL_LETTERS as ReadonlyArray<string>).includes(str); // okay
另一方面,如果您认为includes()
的这种使用应该在没有类型断言的情况下被接受,并且您希望它在您的所有代码中发生,您可以merge in a custom declaration:
// global augmentation needed if your code is in a module
// if your code is not in a module, get rid of "declare global":
declare global
interface ReadonlyArray<T>
includes<U>(x: U & ((T & U) extends never ? never : unknown)): boolean;
这将使数组(嗯,一个只读数组,但这就是您在此示例中所拥有的)将允许.includes()
的任何参数,只要数组元素类型和参数之间存在一些重叠类型。由于string & CapitalLetter
不是never
,它会允许调用。不过,它仍然会禁止CAPITAL_LETTERS.includes(123)
。
好的,希望对您有所帮助;祝你好运!
【讨论】:
标准定义不允许测试更广泛的类型是否有特定原因?使用 const 断言数组测试更广泛的类型不是 .includes 的全部意义吗?如果我已经知道我的值是只读数组的成员(更窄的类型),那么我不需要调用包含这使得签名原样无用不是吗? 我认为如果数组包含文字,那么是的,禁止更广泛的类型似乎很疯狂。但是在像string
或number
这样的情况下,有大量可能的值,那么这是合理的。我认为这不是一个预期的用例,并且允许它的增强更复杂并且可能有一些边缘情况。使用包含检查时,通常很容易扩展数组类型。 ?♂️以上是关于TypeScript const 断言:如何使用 Array.prototype.includes?的主要内容,如果未能解决你的问题,请参考以下文章
在 TypeScript 中将 JSON 文件导入为 const
如何从 url 获取 json 数据并将其保存到 const 变量 [TypeScript]