在 Typescript 中使用“as const”而不添加“readonly”修饰符?

Leo*_*ang 31 javascript typescript visual-studio-code

如果我想定义一个元组或常量对象形状,我会使用as const. 例如:

const arr = [{ a: 1 }] as const;

/* readonly [{
  readonly a: 1;
}] */
Run Code Online (Sandbox Code Playgroud)

然而,我不想要,readonly因为我会得到很多The type 'readonly ...' is 'readonly' and cannot be assigned to the mutable type '...'

我定义了一个“可变”泛型:

type Mutable<T> = {
  -readonly [K in keyof T]: Mutable<T[K]>;
}

type Arr = Mutable<typeof arr>;

/* [Mutable<{
    readonly a: 1;
}>] */
Run Code Online (Sandbox Code Playgroud)

但是,TS 不会递归地应用于MutableVS Code 的类型预览。换句话说,我希望 VS Code 的类型预览显示:

/* [{
    a: 1;
}] */
Run Code Online (Sandbox Code Playgroud)

我认为 VS Code 不可能递归地求值Mutable,因为它可能太慢了。

有没有类似的东西as const,但没有添加readonly

cap*_*ian 5

事实上,您已经使该数组可变了。但您可能已经注意到第一个元素的类型是{a: 1}

const arr = [{ a: 1 }] as const;

type Mutable<T> = {
    -readonly [K in keyof T]:  Mutable<T[K]>;
}

type Arr = Mutable<typeof arr>[0]; // {a: 1}
Run Code Online (Sandbox Code Playgroud)

因为a具有文字类型 number 1,所以您仍然无法改变该元素。

抱歉,可以,但您只能使用1

const x: Arr = [{ a: 1 }]
x[0].a = 1 // ok
Run Code Online (Sandbox Code Playgroud)

为了使其完全可变,您应该将所有文字类型映射到更常见的类型。

1->number


type LiteralToCommon<T extends PropertyKey> =
    T extends number
    ? number : T extends string
    ? string : T extends symbol
    ? symbol : never;

type Mutable<T> = {
    -readonly [K in keyof T]: T[K] extends PropertyKey ? LiteralToCommon<T[K]> : Mutable<T[K]>;
}

const arr = [{ a: 1 }] as const;


type Arr = Mutable<typeof arr>;

const x: Arr = [{ a: 1 }]

x[0].a = 10 // ok

Run Code Online (Sandbox Code Playgroud)

操场

还有一种替代方法:

type Mutable<T> = {
    -readonly [K in keyof T]: Mutable<T[K]>;
}

const arr = [{ a: 1 }];

type ImmutableArr = Readonly<typeof arr>

type MutableArr = Mutable<ImmutableArr>

type Arr = Mutable<typeof arr>;

const x: MutableArr = [{ a: 1 }]

x[0].a = 10 // ok

Run Code Online (Sandbox Code Playgroud)

您可以仅用于Readonly数组。该实用程序将推断{a: 1}{a: number}