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&lt;T&gt;.includes(u) 假定要检查的值与数组元素T 的类型相同或更窄。但在您的情况下,您正在做相反的事情,检查一个 wider 类型的值。事实上,只有在TU 之间没有重叠时(即T &amp; Unever),你才会说Array&lt;T&gt;.includes&lt;U&gt;(x: U) 是一个错误并且必须被禁止。

现在,如果您不打算经常对includes() 进行这种“相反”的使用,并且希望运行时效应为零,那么您应该通过类型断言将CAPITAL_LETTERS 扩大到ReadonlyArray&lt;string&gt;

(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 &amp; CapitalLetter 不是never,它会允许调用。不过,它仍然会禁止CAPITAL_LETTERS.includes(123)

好的,希望对您有所帮助;祝你好运!

【讨论】:

标准定义不允许测试更广泛的类型是否有特定原因?使用 const 断言数组测试更广泛的类型不是 .includes 的全部意义吗?如果我已经知道我的值是只读数组的成员(更窄的类型),那么我不需要调用包含这使得签名原样无用不是吗? 我认为如果数组包含文字,那么是的,禁止更广泛的类型似乎很疯狂。但是在像stringnumber 这样的情况下,有大量可能的值,那么这是合理的。我认为这不是一个预期的用例,并且允许它的增强更复杂并且可能有一些边缘情况。使用包含检查时,通常很容易扩展数组类型。 ?‍♂️

以上是关于TypeScript const 断言:如何使用 Array.prototype.includes?的主要内容,如果未能解决你的问题,请参考以下文章

在 TypeScript 中将 JSON 文件导入为 const

JSX 中的 TypeScript 类型断言

如何从 url 获取 json 数据并将其保存到 const 变量 [TypeScript]

TypeScript 静态类型 const

是否可以在 Typescript 中迭代 const 枚举?

TypeScript专题之类型断言