TypeScript:使用名称未知的字段声明类型或接口
TypeScript: declare a type or an interface with a field that has an unknow name
我必须为团队对象声明一个接口:
export interface Team{
memberUid?: {
mail: string
name: string
photoURL: string
}
startDate: Timestamp
endDate: Timestamp
agenda: Array<{
date: Date | Timestamp
title: string
description: string
}>
}
会有几个成员Uid。我不知道如何在 TypeScript 中声明它。
我试过了:
[memberUid: string] : {
mail: string
name: string
photoURL: string
}
但后来 TypeScript 明白 Team 中的所有字段都应该具有这种结构。
处理这个问题的正确方法是什么?
谢谢
首先让我说我讨厌这种结构。如果有办法将您的代码重写为更符合逻辑的 Team
类型,那就去做吧。但是可以吗?是的,它可以。 Team
必须是 type
而不是接口,因为接口只能有已知的密钥。
让我们从分解开始。
TeamBase 定义了始终存在的属性:
interface TeamBase {
startDate: Timestamp
endDate: Timestamp
agenda: Array<{
date: Date | Timestamp
title: string
description: string
}>
}
UidProperties 定义未知键的内容:
interface UidProperties {
mail: string
name: string
photoURL: string
}
这是一个实用程序类型,可用于通过使用映射类型根据键的类型声明 属性:
type HasProperty<Key extends keyof any, Value> = {
[K in Key]: Value;
}
如果泛型 Key
是一个字符串文字,那么 K in Key
的唯一可能值就是那个确切的字符串,因此 HasProperty<"myKey", string>
解析为 { myKey: string; }
所以现在我们需要定义类型Team
。我们知道它需要包含 TeamBase
的所有属性,并且我们知道有一些值为 UidProperties
的字符串键。之前给您带来问题的一个重要事实是 这个键不能是 TeamBase 的键之一。所以我们不使用 string
,而是使用 Exclude<string, keyof TeamBase>
.
你可以用HasProperty<...>
表示每个不是TeamBase键的字符串键的值为UidProperties
,但我认为最好使用 Partial<HasProperty<...>>
,它表示这些未知字符串键中的任何一个都可以是 UidProperties
,但也可以是 undefined
。当您访问未知密钥时,无论如何您可能都想仔细检查该值。
所以我们最终的团队类型是:
export type Team = TeamBase & Partial<HasProperty<Exclude<string, keyof TeamBase>, UidProperties>>
这按预期工作。因为我们已经考虑到 uid
未定义的可能性,所以如果不通过使用 ?
或 if
的可选链接排除 undefined
,则无法访问 uid
的属性声明等
function f1(team: Team) {
const startDate: Timestamp = team.startDate;
const uid: UidProperties | undefined = team['randomKey']
const name = uid?.name;
if ( uid !== undefined ) {
const mail = uid.mail;
}
}
我必须为团队对象声明一个接口:
export interface Team{
memberUid?: {
mail: string
name: string
photoURL: string
}
startDate: Timestamp
endDate: Timestamp
agenda: Array<{
date: Date | Timestamp
title: string
description: string
}>
}
会有几个成员Uid。我不知道如何在 TypeScript 中声明它。
我试过了:
[memberUid: string] : {
mail: string
name: string
photoURL: string
}
但后来 TypeScript 明白 Team 中的所有字段都应该具有这种结构。
处理这个问题的正确方法是什么?
谢谢
首先让我说我讨厌这种结构。如果有办法将您的代码重写为更符合逻辑的 Team
类型,那就去做吧。但是可以吗?是的,它可以。 Team
必须是 type
而不是接口,因为接口只能有已知的密钥。
让我们从分解开始。
TeamBase 定义了始终存在的属性:
interface TeamBase {
startDate: Timestamp
endDate: Timestamp
agenda: Array<{
date: Date | Timestamp
title: string
description: string
}>
}
UidProperties 定义未知键的内容:
interface UidProperties {
mail: string
name: string
photoURL: string
}
这是一个实用程序类型,可用于通过使用映射类型根据键的类型声明 属性:
type HasProperty<Key extends keyof any, Value> = {
[K in Key]: Value;
}
如果泛型 Key
是一个字符串文字,那么 K in Key
的唯一可能值就是那个确切的字符串,因此 HasProperty<"myKey", string>
解析为 { myKey: string; }
所以现在我们需要定义类型Team
。我们知道它需要包含 TeamBase
的所有属性,并且我们知道有一些值为 UidProperties
的字符串键。之前给您带来问题的一个重要事实是 这个键不能是 TeamBase 的键之一。所以我们不使用 string
,而是使用 Exclude<string, keyof TeamBase>
.
你可以用HasProperty<...>
表示每个不是TeamBase键的字符串键的值为UidProperties
,但我认为最好使用 Partial<HasProperty<...>>
,它表示这些未知字符串键中的任何一个都可以是 UidProperties
,但也可以是 undefined
。当您访问未知密钥时,无论如何您可能都想仔细检查该值。
所以我们最终的团队类型是:
export type Team = TeamBase & Partial<HasProperty<Exclude<string, keyof TeamBase>, UidProperties>>
这按预期工作。因为我们已经考虑到 uid
未定义的可能性,所以如果不通过使用 ?
或 if
的可选链接排除 undefined
,则无法访问 uid
的属性声明等
function f1(team: Team) {
const startDate: Timestamp = team.startDate;
const uid: UidProperties | undefined = team['randomKey']
const name = uid?.name;
if ( uid !== undefined ) {
const mail = uid.mail;
}
}