我想获得动态对象的类型推断。我搜索了该网站,但没有找到我想要的内容。如果有重复的话,我很抱歉。
基本上,我想要一些相同形状的全局常量对象,以便我切换哪个是事件的,但仍然能够在内部具有相同的属性,但只能具有不同的值。
const example = {
constant1: {
value1: 'value1',
value2: 'value2',
value3: 'value3',
},
constant2: {
value1: 'other value1',
value2: 'other value2',
value3: 'other value3',
},
constant3: {
value1: 'another value1',
value2: 'another value2',
value3: 'another value3',
},
};
因此示例对象可以具有任何属性,但它们必须相等,如果我更改其中一个常量内的值之一,我需要它抛出类型错误,但如果我更改其中每个值,没关系。
这是我一开始尝试的方式,它似乎有效,但不是我想要的方式。
interface TypeTest<T> {
[key: string]: T;
}
const CreateTest = <T>(obj: TypeTest<T>) => obj;
const test = CreateTest({
some: {
other: 'test',
},
thing: {
other: 'testing',
},
});
type constants = keyof typeof test;
由于接口(interface)无法进行类型推断,因此我使用了一个创建者函数来接收对象并返回它的类型。但它不会键入内部对象,因为 typescript 会推断每个内部对象的联合类型。另外我没有得到任何类型的外部对象,因为
type constants = string | number
为了解决这个问题,我尝试了以下方法:
type TypeTest<D, T> = {
[key in keyof D]: T;
};
const CreateTest = <D, T>(obj: TypeTest<D, T>) => obj;
const test = CreateTest({
some: {
other: 'test',
},
thing: {
other: 'testing',
},
});
type constants = keyof typeof test;
现在我得到了外部对象的正确类型( type constants = "some" | "thing"
),但完全丢失了内部对象的类型推断,因为现在我只有未知类型,即使我使用 [key2 in keyof T]: string
至于内部物体的形状,目前还不得而知。
我能够解决所有这些问题,但最终开发人员的体验并不是那么好。
interface TypeTest<T extends readonly string[], U extends readonly string[]> {
outer: T;
inner: U;
values: {
[Property1 in T[number]]: {
[Property2 in U[number]]: string;
};
};
}
const CreateTest
= <
T extends readonly string[],
U extends readonly string[],
> (constants: TypeTest<T, U>) => constants.values;
const test = CreateTest({
outer: [ 'some', 'thing' ] as const,
inner: [ 'other' ] as const,
values: {
some: {
other: 'test',
},
thing: {
other: 'testing',
},
},
});
type constants = keyof typeof test;
type values = keyof typeof test[constants];
现在我传递了两个数组,它们仅用于获取类型并在之后丢弃,两个数组也必须被断言为 const,否则我只能得到 string[]
输入它们。
那么,有没有办法将对象传递给 CreateTest()
函数,并从中获取键入的值?和
type constants = "some" | "thing"
和
type values = "other
编辑:
嵌套级别仅为 2 层。但有可能获得更多吗?
还有可能获得不同级别的嵌套吗?就像一个对象有 3 层嵌套,而其他对象有 5 层。
我不需要那样,我只是好奇。
最佳答案
一种方法是编写 createTest()
辅助函数采用单个参数 obj
,一个generic type T
我们在哪里constrain T
到一个对象类型,其中所有属性都是相同类型,特别是 intersection来自 T
的属性.
当您尝试将所有属性限制为同一类型时,编译器推断出 union的所有属性。这通常是可取的,因为它是尽可能宽松的; {a: A, b: B}
类型的值始终可分配给类型 {a: A | B, b: A | B}
。但你只想接受A
的东西和B
已经彼此同意了。通过将约束切换为 {a: A & B, b: A & B}
它将强制执行。
无论如何,我们可以将其实现为 recursive constraint
const createTest = <T extends IntersectProps<T>>(obj: T) => obj;
哪里IntersectProps<T>
定义为
type IntersectProps<T> =
{ [K in keyof T]: (x: T[K]) => void }[keyof T] extends
(x: infer I) => void ? Record<keyof T, I> : never;
它的工作原理与 this question 中的并集到交集代码类似。 ;我们遍历每个属性,将其置于逆变位置,然后从中进行推断。另一个问题对此进行了更多解释。但您可以亲眼看到这种类型确实与属性相交:
interface A { x: 1 };
interface B { y: 2 };
type Example = IntersectProps<{ a: A, b: B }>;
/* type Example = {
a: A & B;
b: A & B;
} */
好吧,让我们尝试一下:
const badTest = createTest({
a: { b: 1, c: "", d: true },
// ------> ~ <--- error
e: { b: 2, c: 3, d: false }
// ------> ~ <--- error
})
失败是因为交集类型 {b: number, c: string, d: boolean} & {b: number, c: number, d: boolean}
相当于 {b: number, c: never, d: boolean}
,以及c
a
中的每个属性和e
属性无法分配给 never
。您可以通过更改 c
之一来修复它属性与其他属性的类型相同:
const goodTest = createTest({
a: { b: 1, c: "", d: true },
e: { b: 2, c: "z", d: false }
}) // okay
所以现在您可以获得所需的行为,而无需在 createTest()
的参数中添加冗余信息。 :
const test = createTest({
some: {
other: 'test',
},
thing: {
other: "okay",
},
});
type Constants = keyof typeof test;
// type Constants = "some" | "thing"
关于typescript - 如何获取嵌套对象的类型推断?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72538765/