从可区分联合派生 TypeScript 接口
Derive TypeScript interface from discriminiated union
考虑以下 TypeScript 代码:
type AccountType = "staff" | "user"
type StaffAccountName = "owner" | "CEO" | "accountant"
type UserIdentifier =
| { accountType: "user"; id: number }
| { accountType: "staff"; id: StaffAccountName };
我现在想制作一个字典来存储每个用户的年龄,格式为
dict[accountType][id] = "age"
如何动态输入这样的字典?我在想
type Dictionary<K,V> = Partial<Record<K,V>>
type AgeDict<UI extends UserIdentifier> = {
[UI.accountType]: Dictionary<UI.id, number>
}
...这显然行不通。
我真的很想避免重新定义允许的类型对,例如:
type AgeDict<UI extends UserIdentifier> = {
"user": Dictionary<number, number>,
"staff": Dictionary<StaffAccountName, number>
}
因为我希望他们能够依赖单一的事实来源。
编辑: Jcalz 的回答在技术上允许我创建界面,但是 I get an error 当我尝试访问它时:
const getAge = (identifier: UserIdentifier, dict: AgeDict) => dict[identifier.accountType][identifier.id]
// ^
// Element implicitly has an 'any' type because expression of type 'number | "owner" | "CEO" | "accountant"' can't be used to index type 'Partial<Record<StaffAccountName, number>> | Partial<Record<number, number>>'.
// No index signature with a parameter of type 'number' was found on type 'Partial<Record<StaffAccountName, number>> | Partial<Record<number, number>>'.(7053)
我还需要能够推断出上述函数有效的解决方案。
我建议使用映射类型。特别是,您可以使用 TypeScript 4.1 中引入的 mapped type key remapping via as
,如下所示:
type AgeDict = {
[UI in UserIdentifier as UI['accountType']]: Dictionary<UI['id'], number>
}
这在概念上与您所做的相同。您只是错过了它需要是一个映射类型(而不是计算键/索引签名的东西)并且要在您需要 a lookup type 的类型中查找 属性 ,它使用方括号表示法 UI['accountType']
而不是像 UI.accountType
这样的点分符号(不支持,因为它与类型命名空间冲突)。
让我们看看它是否如您所愿:
/* type AgeDict = {
user: Partial<Record<number, number>>;
staff: Partial<Record<StaffAccountName, number>>;
} */
const a: AgeDict = {
staff: {
CEO: 50,
owner: 45,
accountant: 40
},
user: {
1: 35,
2: 30,
}
}
看起来不错。
考虑以下 TypeScript 代码:
type AccountType = "staff" | "user"
type StaffAccountName = "owner" | "CEO" | "accountant"
type UserIdentifier =
| { accountType: "user"; id: number }
| { accountType: "staff"; id: StaffAccountName };
我现在想制作一个字典来存储每个用户的年龄,格式为
dict[accountType][id] = "age"
如何动态输入这样的字典?我在想
type Dictionary<K,V> = Partial<Record<K,V>>
type AgeDict<UI extends UserIdentifier> = {
[UI.accountType]: Dictionary<UI.id, number>
}
...这显然行不通。
我真的很想避免重新定义允许的类型对,例如:
type AgeDict<UI extends UserIdentifier> = {
"user": Dictionary<number, number>,
"staff": Dictionary<StaffAccountName, number>
}
因为我希望他们能够依赖单一的事实来源。
编辑: Jcalz 的回答在技术上允许我创建界面,但是 I get an error 当我尝试访问它时:
const getAge = (identifier: UserIdentifier, dict: AgeDict) => dict[identifier.accountType][identifier.id]
// ^
// Element implicitly has an 'any' type because expression of type 'number | "owner" | "CEO" | "accountant"' can't be used to index type 'Partial<Record<StaffAccountName, number>> | Partial<Record<number, number>>'.
// No index signature with a parameter of type 'number' was found on type 'Partial<Record<StaffAccountName, number>> | Partial<Record<number, number>>'.(7053)
我还需要能够推断出上述函数有效的解决方案。
我建议使用映射类型。特别是,您可以使用 TypeScript 4.1 中引入的 mapped type key remapping via as
,如下所示:
type AgeDict = {
[UI in UserIdentifier as UI['accountType']]: Dictionary<UI['id'], number>
}
这在概念上与您所做的相同。您只是错过了它需要是一个映射类型(而不是计算键/索引签名的东西)并且要在您需要 a lookup type 的类型中查找 属性 ,它使用方括号表示法 UI['accountType']
而不是像 UI.accountType
这样的点分符号(不支持,因为它与类型命名空间冲突)。
让我们看看它是否如您所愿:
/* type AgeDict = {
user: Partial<Record<number, number>>;
staff: Partial<Record<StaffAccountName, number>>;
} */
const a: AgeDict = {
staff: {
CEO: 50,
owner: 45,
accountant: 40
},
user: {
1: 35,
2: 30,
}
}
看起来不错。