typescript - 如何获取嵌套对象的类型推断?

标签 typescript typescript-generics

我想获得动态对象的类型推断。我搜索了该网站,但没有找到我想要的内容。如果有重复的话,我很抱歉。

基本上,我想要一些相同形状的全局常量对象,以便我切换哪个是事件的,但仍然能够在内部具有相同的属性,但只能具有不同的值。

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"

Playground link to code

关于typescript - 如何获取嵌套对象的类型推断?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72538765/

相关文章:

javascript - ExpressJS 路由无法解析

javascript - routerLink 在 angular2 中使用 Protractor 的 e2e 测试中不起作用

typescript - 从 typescript 模块导出所有类型(并且只是类型)

javascript - 数组填充后如何加载页面?

typescript - Typescript 中的类型化通用键值接口(interface)

javascript - 尝试...catch 与 .catch

typescript 泛型 : checking a unique property on a generic type object in if condition doesn't determine the object's type

typescript - 从传递函数的返回值推断函数泛型类型 U

generics - typescript :实现通用接口(interface)

typescript :带枚举的通用函数