【问题标题】:Use string literal array as type使用字符串文字数组作为类型
【发布时间】:2020-03-02 02:19:34
【问题描述】:

我想将单个数组 const fruits = ['banana', 'apple', 'orange'] 用作普通数组,并用作 type

我应该能够做到这一点: const x: fruits // => only accepts 'banana', 'apple' or 'orange'

并且还能够做到这一点: @IsIn(fruits)


我尝试将数组声明为<const>,如:

const fruits = <const>['banana', 'apple', 'orange']
type Fruits = typeof fruits[number] // this evaluates to type: "banana" | "apple" | "orange"

但是@IsIn(fruits)会返回如下错误:

Argument of type 'readonly ["banana", "apple", "orange"]' is not assignable to parameter of type 'any[]'.
  The type 'readonly ["banana", "apple", "orange"]' is 'readonly' and cannot be assigned to the mutable type 'any[]'.ts(2345)

所以我想如果我创建了两个数组,一个普通数组和一个只读数组,它应该可以工作。所以我尝试了这个:

const fruits = ['banana', 'apple', 'orange']
const fruits_readonly: <const>[...fruits]
type Fruits = typeof fruits_readonly[number]

但现在Fruits 评估为type: string 而不是type: "banana" | "apple" | "orange"

【问题讨论】:

    标签: typescript class-validator


    【解决方案1】:

    const assertions 确实会生成带有readonly 元素的对象和数组。如果您想获得const 断言为您带来的字符串文字和元组类型的好处,同时也取消readonly-ing 结果,您可以编写一个辅助函数来做到这一点。我就叫它mutable()

    const mutable = <T>(t: T): { -readonly [K in keyof T]: T[K] } => t
    
    const fruits = mutable(['banana', 'apple', 'orange'] as const);
    // const fruits: ["banana", "apple", "orange"]
    

    这将工作一层深。如果您有嵌套的对象/数组类型,您可能想要创建一个 DeepMutable 类型和 deepMutable() 辅助函数:

    type DeepMutable<T> =
        T extends object ? { -readonly [K in keyof T]: DeepMutable<T[K]> } : T
    
    const deepMutable = <T>(t: T) => t as DeepMutable<T>;
    

    上述情况也一样,

    const alsoFruits = deepMutable(['banana', 'apple', 'orange'] as const);
    // const alsoFruits: ["banana", "apple", "orange"]
    

    但是嵌套对象的区别变得很重要:

    const readonlyDeepFruits = {
        yellow: ["banana", "lemon"],
        red: ["cherry", "apple"],
        orange: ["orange", "mango"],
        green: ["lime", "watermelon"]
    } as const;
    /* const readonlyDeepFruits: {
        readonly yellow: readonly ["banana", "lemon"];
        readonly red: readonly ["cherry", "apple"];
        readonly orange: readonly ["orange", "mango"];
        readonly green: readonly ["lime", "watermelon"];
    } */
    
    const partiallyMutableDeepFruits = mutable(readonlyDeepFruits);
    /* const partiallyMutableDeepFruits: {
        yellow: readonly ["banana", "lemon"];
        red: readonly ["cherry", "apple"];
        orange: readonly ["orange", "mango"];
        green: readonly ["lime", "watermelon"];
    } */
    
    const fullyMutableDeepFruits = deepMutable(readonlyDeepFruits);
    /* const fullyMutableDeepFruits: {
        yellow: ["banana", "lemon"];
        red: ["cherry", "apple"];
        orange: ["orange", "mango"];
        green: ["lime", "watermelon"];
    } */
    

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

    Link to code

    【讨论】:

    • 谢谢,这行得通 :) 但我仍然不明白为什么 typeof fruits_readonly[number] 不能评估为 type: "banana" | "apple" | "orange"。请给我解释一下好吗?
    • 哦,对不起...在const fruits = ['banana', 'apple', 'orange'] 中,fruits 被推断为string[] 类型,因此编译器已经忘记了传递给它的特定字符串的标识。所以[...fruits] 也将是string[]。如果您希望编译器记住"banana" | "apple" | "orange",您需要告诉它不要在它仍然拥有信息时丢弃它......这就是as const 所做的(除其他外)。
    猜你喜欢
    • 1970-01-01
    • 2017-11-13
    • 1970-01-01
    • 2016-08-17
    • 2019-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多