我正在尝试将索引签名类型添加到嵌套对象,以便我可以通过计算值访问它的一些属性。
例如,给定这个对象:
export const english = {
home: {
title: 'Title',
characters_list: 'Characters list',
moves: 'Moves',
header: 'Home',
search: 'Search',
},
character: {
detail: {
about: {
title: 'About',
info: 'Info',
full_name: 'Full name',
status: 'Status',
gender: 'Gender',
},
origin: {
title: 'Origin',
origin: 'Origin',
current_location: 'Current location',
},
},
},
}
我想像这样访问它的属性:
const key = 'moves';
english.home[key]
但我无法正确理解。到目前为止我已经能够输入第一层了
export type Translation = {
[key: string]: typeof english[keyof typeof english]
}
我认为这需要某种递归方法,但我不知道如何实现
最佳答案
好的,我想我知道我们需要做什么。问题在于对象允许键为 string
类型中的每个值,但是一旦定义了类型,您就可以使用 string
的子集。因此,TypeScript 会抛出一个错误(理所当然),您不能只使用每个字符串来访问,而只能使用作为有效键的字符串(例如 moves
、 >标题
等)
因此,在某个时间点,您必须确保要访问的 key 有效。
以下是可能性
a) 带有 const 上下文的字符串文字
你分配的那一刻
const key = 'moves'
key
不是 string
类型,而是 'moves'
类型,一种非常具体的值类型(或文字类型)和子类型-字符串
的类型。此访问应该有效
const key = 'moves'
english.home[key] // 👍
或者,将它们定义为 const 以获得相同的效果
let key = 'moves' as const // also 'moves', not string
与通用选择功能相同的行为
function pick<T extends Object, K extends keyof T>(o: T, k: K) {
return o[k]
}
pick(english.home, 'moves')
pick(english, 'home')
pick(pick(english, 'home'), 'moves')
这与直接属性访问基本相同,但您可以让通用约束帮助您找到正确的类型。
b) 输入谓词来缩小范围
但是您使用的不是字符串文字,而是计算出的键。这意味着您必须以某种方式从大的字符串集转移到较小的实际键集。您可以使用一个函数来检查此键是否有效(无论如何您都应该这样做),并使用一个类型谓词来缩小控制流中的类型范围(您很快就会看到它是什么)来做到这一点
想象一下这样的辅助函数:
function isKeyOf<O extends Object>(o: O, k: string | number | symbol): k is keyof O {
return typeof k === "string" && Object.keys(o).indexOf(k) >= 0
}
这个函数中发生了很多事情。
- 我们定义了一个名为 O 的泛型,它通过成为对象来进行约束
- 我们允许传入一个键
k
,它可以是所有可能的对象键类型 - 然后我们检查它是否是正确的字符串属性访问器以及它是否在键列表中。如果是这样,我们可以有把握地说 k 是
keyof O
类型
类型谓词适用于返回 bool 值的函数。如果此条件为真,则类型将变为不同的内容(即 keyof O
)
您可以使用它创建一个 pickSafe
函数,您可以在其中传递任何 key
function pickSafe<T extends Object>(o: T, k: string) {
if (isKeyOf(o, k)) {
return o[k]
}
throw new Error('Not accessible')
}
pickSafe(english.home, 'moves')
pickSafe(english, 'home')
pickSafe(pickSafe(english, 'home'), 'moves')
问题是,虽然此访问有效,但您会丢失正在访问的对象的信息。那么您需要进行运行时类型检查。做
const res = pickSafe(english, 'home')
.. 将鼠标悬停在 res
上,您会看到由此产生的可能类型
这实际上是期望的行为,因为它告诉您应用程序中的哪些地方可能会变得不清楚。
如果您确实知道您的应用程序可以正常运行并且计算出的键就是它们的样子,那么您始终可以进行类型转换来覆盖 TypeScript 的不确定性
const keyey = myRandomString as keyof typeof english.home
pick(english.home, keyey)
这是一个供你玩耍的 Playground fiddle around .
我希望这有帮助!
关于typescript - TypeScript 中的递归索引签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62777709/