诚然,我对 TypeScript 还很陌生,我正在尝试弄清楚如何创建一个通用共享库函数 getFlags
,它接受一个字符串标识符数组并返回一个对象,其键是标识符,其属性是标志对象。除了它们的 config
属性...外,所有标志对象都具有相似的形状。
例如:
const flags = getFlags(['flagA', 'flagB'])
结果 flags
看起来像这样
{
flagA: {
name: 'a',
value: 'test1',
config: {
foo: 'red'
}
},
flagB: {
name: 'b',
value: 'test2',
config: {
bar: 123
}
},
// ....
}
我最初尝试为此定义类型的尝试如下所示:
type Flag<T> {
name: string,
value: string,
config: T
}
type FlagList = {
[key: string]: Flag<unknown>
}
type ConfigA {
foo: string
}
type ConfigB {
bar: number
}
我想要的是一种创建 getFlags
的通用版本的方法,以便 Typescript 知道返回的每个标志的基础配置属性的类型。
const {flagA, flagB} = getFlags(['flagA','flagB'])
flagA.config.foo // OK
flagA.config.bar // Invalid
flagB.config.bar // OK
flagB.config.foo // Invalid
const {flagA, flagB, flagC, flagD} = getFlags(['flagA','flagB' 'flagC', 'flagD'])
getFlags
函数看起来像这样:
function getFlags(ids: string[]): FlagList {
return retrieveFlagsByIds(ids)
}
但是,我不确定如何告诉 TypeScript 配置属性的类型信息。一种想法是像这样以某种方式传递类型。然而,可能有任意数量的这些标志,这似乎有点疯狂:
const { flagA, flagB } = getFlags<ConfigA, ConfigB>(['flagA','flagB'])
const { flagA, flagB, flagC, flagD, flagE } = getFlags<ConfigA, ConfigB, ConfigC, ConfigD, ConfigE>(['flagA','flagB', 'flagC', 'flagD', 'flagE'])
因为 getFlags
是一个共享库函数,所以不可能知道消费者可能拥有的所有标志配置类型。有什么想法吗?
答案 0 :(得分:3)
我倾向于创建一个辅助对象类型 FlagConfigs
来表示标志名称和配置类型之间的映射:
interface FlagConfigs {
flagA: { foo: string };
flagB: { bar: number };
flagC: { baz: boolean }
}
那么 getFlags()
的调用签名将如下所示:
declare function getFlags<K extends keyof FlagConfigs>(flags: K[]): {
[P in K]: Flag<FlagConfigs[P]>
};
这里我们在 K
中创建函数 generic,FlagConfigs
的键的 union。这个联合看起来像 "flagA" | "flagB"
等,所以它会为 flags
数组中的每个不同值都有一个成员,这就是处理可能无限数量类型的问题的原因参数。单个 K
类型参数可以表示所有这些。对于返回类型,我们将 K
通过 mapping 中的每个标志类型 P
转换为一个属性,其中 P
是键,其中 Flag<FlagConfigs[P]>
是值类型。后一种类型的意思是“在 P
中查找 FlagConfigs
键,获取该属性,并将其用作 config
中的 Flag
属性类型。
让我们看看它是否有效:
const {flagA, flagB} = getFlags(['flagA','flagB'])
flagA.config.foo // okay
flagA.config.bar // error!
flagB.config.bar // okay
flagB.config.foo // error!
看起来不错。
至于 getFlags()
的实现,这取决于您。您可能需要使用 type assertions 使编译器相信您的实现实际上符合调用签名。不太可能自行验证这一点。所以要小心。