// common.js
const boxNames = ['one', 'two'];
module.exports {
boxNames,
}
const common = require("./common.js");
const boxNames = common.boxNames;
const messages = {
'one': {
id: 'some id',
text: 'some text',
},
'two': {
id: 'some id',
text: 'some text',
},
};
const disabled = boxNames.map((key: string) => messages[key]);
上面的代码为 messages[key]
语句产生了 TypeScript 错误:
TS7053:元素隐式具有“任何”类型,因为表达式为 类型 'string' 不能用于索引类型 '{ one: { id: string;文本: 细绳; };二:{id:字符串;文本:字符串; }; }'。无索引 在类型 '{ 一: { ID:字符串;文本:字符串; };二:{id:字符串;文本:字符串; }; }'。
我将 key
声明为字符串,而 messages
的键是字符串,所以有什么问题?
(此处值得注意的是,messages
是使用 defineMessages
库中的 react-intl
方法生成的。)
(我在 StackOverflow 上看到过一些与 Object.keys 类似的答案,但我不确定这与我的 TypeScript 错误有什么关系。)
答案 0 :(得分:3)
对象 messages
的键不是 string
类型,而是 "one" | "two"
类型。换句话说,它们是字符串字面量,它是一种比简单的 string
更窄的类型,并且不能被更广泛的 string
类型索引。
编译示例的一种简单方法是将键数组转换为元组:
const boxNames = ['one', 'two'] as const;
这有效地将类型从 string[]
更改为 ["one", "two"]
,现在当您映射它们时,它们将是正确的类型来索引您的对象。附带说明一下,没有必要显式设置 key
的类型,因为这可以从用法中推断出来。
const disabled = boxNames.map((key) => messages[key]); // this is now fine
另一种输入方式是将 messages
的键分配给数组:
const messages = {
'one': {
id: 'some id',
text: 'some text',
},
'two': {
id: 'some id',
text: 'some text',
},
};
const boxNames: (keyof typeof messages)[] = ['one', 'two'];
const disabled = boxNames.map((key) => messages[key]);
这比使用元组更具动态性,并且它将保持类型安全,因为您将不允许在 boxNames
中包含比 messages
键存在的字符串更多的字符串。我想象这仍然适用于您所描述的 require
场景,尽管我现在没有设置环境来测试它。
我认为这里的主要内容是 boxNames
需要获得更具体的类型,或者 messages
需要不太具体的类型。我假设这些键必须完全匹配,但也许你的情况实际上更适合通过分配 messages
一个 string
索引类型 - 我认为这还不够上下文在这里假设任何一种方式。这意味着处理代码中的可能性,即您实际上并不知道数组和对象在运行时是否匹配。
答案 1 :(得分:1)
Typescript 不知道 boxNames 的元素是 messages
的键。
string
和 keyof
之间有明显的区别。
有几种方法可以告诉 TS 字符串是对象的键。
无需亲自测试,您也许可以做到这一点:
boxNames.map((key: keyof typeof messages) => messages[key])
要查询如何使用keyof
关键字,需要在严格模式下索引对象。
答案 2 :(得分:0)
当你创建一个像下面这样的对象而不给出类型时,该对象的推断类型将是 Record<'one' | 'two', string>
。
const numberCounter = {
one: 'one',
two: 'two'
}
所以,我们在这里必须记住,对象的键总是文字类型的联合,对象的值将是值类型的联合。
在下面的例子中,键是'一'和'二'的并集,值只是字符串类型(字符串和字符串的并集是一个字符串。)
我曾经像下面这样输入对象,所以我从来没有遇到过这样的问题。这里我明确说我的对象键是字符串类型的。
const boxNames = ['one', 'two'] ;
interface Type {
id: string;
text: string;
}
const messages: Record<string, Type> = {
one: {
id: 'some id',
text: 'some text',
},
two: {
id: 'some id',
text: 'some text',
},
};
// key is already inferred as string
const disabled = boxNames.map(key => messages[key]);
console.log(disabled);
如果您不想明确说明对象键是字符串,那么@lawrence-witt answer 最适合这里。
还有一种方法可以使键成为“一”和“二”的并集。
const boxNames = ['one', 'two'];
const messages = {
one: {
id: 'some id',
text: 'some text',
},
two: {
id: 'some id',
text: 'some text',
},
};
type Key = keyof typeof messages;
// here we are doing type cast of string into union of `one` and `two`.
//boxNames key are of string type
const disabled = boxNames.map(key => messages[key as Key]);
console.log(disabled);