TypeScript const 断言:如何使用 Array.prototype.includes?
TypeScript const assertions: how to use Array.prototype.includes?
我正在尝试使用一个元素数组作为联合类型,这在 TS 3.4 中使用 const 断言变得很容易,所以我可以这样做:
const CAPITAL_LETTERS = ['A', 'B', 'C', ..., 'Z'] as const;
type CapitalLetter = typeof CAPITAL_LETTERS[string];
现在我想测试一个字符串是否是大写字母,但是以下失败 "not assignable to parameter of type":
let str: string;
...
CAPITAL_LETTERS.includes(str);
有没有比将 CAPITAL_LETTERS
转换为 unknown
然后再转换为 Array<string>
更好的方法来解决这个问题?
The standard library signature for Array<T>.includes(u)
假定要检查的值与数组元素 T
的类型相同或更窄。但是在您的情况下,您正在做相反的事情,检查 wider 类型的值。事实上,只有在 T
和 U
之间没有重叠(即 T & U
是never
)。
现在,如果您不打算经常使用 includes()
进行这种 "opposite",并且您希望零运行时效果,则应该扩大 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)
。
好的,希望对您有所帮助;祝你好运!
另一种解决方法是使用类型保护
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
}
这是一个适用于使用 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。
使用 lodash
const CAPITAL_LETTERS = ['A', 'B', 'C', 'Z'] as const;
_.includes(CAPITAL_LETTERS, 'A');
我正在尝试使用一个元素数组作为联合类型,这在 TS 3.4 中使用 const 断言变得很容易,所以我可以这样做:
const CAPITAL_LETTERS = ['A', 'B', 'C', ..., 'Z'] as const;
type CapitalLetter = typeof CAPITAL_LETTERS[string];
现在我想测试一个字符串是否是大写字母,但是以下失败 "not assignable to parameter of type":
let str: string;
...
CAPITAL_LETTERS.includes(str);
有没有比将 CAPITAL_LETTERS
转换为 unknown
然后再转换为 Array<string>
更好的方法来解决这个问题?
The standard library signature for Array<T>.includes(u)
假定要检查的值与数组元素 T
的类型相同或更窄。但是在您的情况下,您正在做相反的事情,检查 wider 类型的值。事实上,只有在 T
和 U
之间没有重叠(即 T & U
是never
)。
现在,如果您不打算经常使用 includes()
进行这种 "opposite",并且您希望零运行时效果,则应该扩大 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)
。
好的,希望对您有所帮助;祝你好运!
另一种解决方法是使用类型保护
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
}
这是一个适用于使用 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。
使用 lodash
const CAPITAL_LETTERS = ['A', 'B', 'C', 'Z'] as const;
_.includes(CAPITAL_LETTERS, 'A');