TypeScript:as const字符串数组字段导致只读错误。

4
以下示例在 test 处存在 TypeScript 错误。
type Test = {
    obj: object;
    arr: string[];
};

export const test: Test = {
    obj: {},
    arr: ['foo'],
} as const;

错误提示:

Type '{ readonly obj: {}; readonly arr: readonly ["foo"]; }' is not assignable to type 'Test'. Types of property 'arr' are incompatible. The type 'readonly ["foo"]' is 'readonly' and cannot be assigned to the mutable type 'string[]'.

我想在 test 中使用 as const(导致错误),因为 test 不应该被更改。与此同时,我将拥有其他可变的类型为 Test 的变量,因此我不能让任何一个字段是 readonly。如何使错误消失并实现我的目标?

令人困惑的是,obj 没有引起错误。数组在技术上是对象。


1
不支持使用 as const 断言的显式类型。您应该选择使用其中之一。 - captain-yossarian from Ukraine
这个回答解决了你的问题吗?TypeScript Readonly类型的不一致性 - Vega
还有很多更多的。 - Vega
据我所知,问题在于 as constarr 的类型缩小为一个元组(固定大小的数组),其元素为字符串字面量类型 - 在这种情况下是 ['foo']。由于 ['foo']string[] 更具体,因此不被允许。这样理解正确吗? - GregL
4个回答

1

您能使用Readonly实用程序类型吗?

改编后的答案

Playground链接

@GregL指出,这不会阻止数组被push突变,因此我建议使用DeepReadonly,它将递归地将Readonly应用于所有基于对象的子项。

type Test = {
    obj: object;
    arr: string[];
};

type DeepReadonly<T> = {
    readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : Readonly<T[P]>
}

const test: DeepReadonly<Test> = {
    obj: {},
    arr: ['foo'],
}

test.obj = {}

test.arr = []

test.arr.push('')

之前的回答

只读文档

试用链接

type Test = {
    obj: object;
    arr: string[];
};

const test: Readonly<Test> = {
    obj: {},
    arr: ['foo'],
}

// Error
test.obj = {}

// Error
test.arr = []

这样可以保留原始类型,但不允许更改变量。

1
这种方法的缺点是,当我认为 OP 希望它出错时,test.arr.push('bar') 不会报错。 - GregL
@GregL 我已经根据你的评论修改了我的答案,谢谢。你在修改后的答案中看到任何问题吗? - N.J.Dawson

1

这似乎有效:

type Test = {
    obj: object;
    arr: string[];
};

export const test: Test = {
    obj: {},
    arr: ['foo'] as string[]
} as const;

0
你需要在Test中将arr字段的类型添加为readonly。
type Test = {
    str: string;
    arr: readonly string[];
};

编辑:鉴于您希望保持Test arr字段可变,您可以尝试这样做。将arr设置为string[]readonly string[]的联合类型。

然而,您会立即遇到其他问题,因为如果TypeScript不知道Test的给定实例中arr具有哪种类型,它将假定最严格的行为并强制执行您实际上无法改变数组的限制。

解决方案是拥有像下面的pushIfNotReadonly()这样的实用函数,它可以使用类型保护来确保给定的数组实际上是可变的,然后再尝试进行此类操作。但“失败”的行为有点未定义-也许您只是不将值推入数组中,但是如何在逻辑中考虑这种棘手的行为?您很快就会失去对正在发生的事情的掌握,有用的类型检查也会消失,从而使自己陷入困境。

您正在尝试的内容与TypeScript模式不太兼容,如果可以的话应该避免。我建议您重新考虑如何使用类型定义以及您的基本意图,并评估其他选项。

type Test = {
    obj: object;
    arr: string[] | readonly string[];
};

export const test1: Test = {
    obj: {},
    arr: ['foo'],
} as const;

export const test2: Test = {
    obj: {},
    arr: ['bar'],
}

function pushIfNotReadonly(arr: Test['arr'], value: string) {
    if (Array.isArray(arr)) {
        arr.push(value)
    }
}

pushIfNotReadonly(test1.arr, 'string'); // will not push because it is readonly
pushIfNotReadonly(test2.arr, 'string'); // will push because it is NOT readonly

我不想让那个字段只读。我会在我的问题中添加这个要求。 - sdfsdf
@sdfsdf 已更新 - jered
谢谢。我认为你是对的,这与TypeScript模式不兼容。我认为最好删除as const并希望对象永远不会被改变。 - sdfsdf

0
type Test = {
  str: string;
  arr: string[];
};

export const test: Test = {
  str: 'Hello',
  arr: ['foo'] as string[],
} as const;

如果你在 Test 中使用了 as const,那么你需要在 read-only 内定义类型,更多细节请参考此文档 TypeScript 只读属性


我已更新问题。请不要使用只读字段。 - sdfsdf
你可以使用 as string[] - Dhruv Harsora

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接