【问题标题】:TypeScript get type of a specific property from a nested array with objectsTypeScript 从带有对象的嵌套数组中获取特定属性的类型
【发布时间】:2022-11-13 08:30:34
【问题描述】:

我有以下类型的对象,我需要一个 TypeScript 助手来获取字段名的类型:

const config: FormConfig = [
    {
        type: "row",
        content: [
            {
                type: "text",
                name: "firstName",
                fieldConfig: {
                    label: "First Name",
                },
            },
            {
                type: "text",
                name: "lastName",
                fieldConfig: {
                    label: "Last Name",
                },
            },
        ],
    },
]

export interface FormRowConfig {
    type: "row";
    content: FormEntryConfig[];
}

export interface FormFieldText {
    type: "text";
    name: string;
    ...
}

export type FormEntryConfig = FormRowConfig | FormFieldText // plus other fields;

export type FormConfig = FormEntryConfig[];

我尝试了以下方法来获取条目的名称,但我得到“类型实例化过深并且可能无限”。 (我猜这是有道理的):

type SomeHelper<Entry extends FormEntryConfig> = Entry extends FormFieldConfig
    ? Entry["name"]
    : Entry extends FormRowConfig
    ? SomeHelper<Entry>
    : never;

我什至能够得到例如"firstName" 是这样的类型,因为配置是强类型的并且名称设置为字符串?

我如何实现这样的目标?

type Name = SomeHelper<typeof config> // "firstName" | "lastName"

【问题讨论】:

  • 您可以将所有这些类型添加到TypeScript playground 中以便我们重现它吗?
  • 可以创建这样的类型。请参阅此Playground。但是需要做一些修改,比如去掉config的类型注解,增加as const。这会回答你的问题吗?
  • @TobiasS。是的,这有很大帮助,谢谢!
  • @JohnnyKontrolletti - 很高兴听到:)让我写一个答案

标签: typescript


【解决方案1】:

目前,config 的显式类型会删除您要提取的类型信息。我们需要删除类型注释以让 TypeScript 推断类型。

我们还需要添加as const,这样name 属性的类型就不仅仅是string,而是窄字符串文字类型。

const config = [
    {
        type: "row",
        content: [
            {
                type: "text",
                name: "firstName",
                fieldConfig: {
                    label: "First Name",
                },
            },
            {
                type: "text",
                name: "lastName",
                fieldConfig: {
                    label: "Last Name",
                },
            },
        ],
    },
] as const

as const 创建数组 readonly。我们需要修改FormRowConfig 接口和SomeHelper 的通用约束来解决这个问题。

export interface FormRowConfig {
    type: "row";
    content: readonly FormEntryConfig[];
}

type SomeHelper<T extends readonly FormEntryConfig[]> = /* ... */

现在到实际的SomeHelper 实现。

type SomeHelper<T extends readonly FormEntryConfig[]> = 
  T[number] extends infer U
    ? U extends FormFieldText
      ? U["name"]
      : U extends FormRowConfig
        ? SomeHelper<U["content"]>
        : never
    : never

它类似于您在问题中的内容。但它现在接受一个元组(因为config 有一个元组类型)并正确处理它。

这会给我们正确的结果:

type Name = SomeHelper<typeof config>
// type Name = "firstName" | "lastName"

Playground

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-17
    • 2019-03-23
    • 2021-03-08
    相关资源
    最近更新 更多