【问题标题】:How to extract "string" property names from an interface in Typescript?如何从 Typescript 中的接口中提取“字符串”属性名称?
【发布时间】:2020-10-06 15:47:15
【问题描述】:

Typescript playground

我想从SOME_OBJECT 中提取string 属性并将其作为联合类型。因此,在下面的示例中,我希望 STRING_KEYS 的类型为 "title" | "label"

interface SOME_OBJECT {
    title:      string,
    label:      string,
    someBool:   boolean,
    someDate:   Date,
    someNumber: number
}

type ExtractString<T> = keyof T extends string ? keyof T : never;

type STRING_KEYS = ExtractString<SOME_OBJECT>  // <----- THIS SHOULD BE "title" | "label"

这就是我得到的:

我认为我在这里的方向是正确的(通过使用条件类型),但我还没有安静下来。实现这一目标的最佳方法是什么?

【问题讨论】:

    标签: typescript typescript-typings conditional-types


    【解决方案1】:

    Typescript Docs: Distributive Conditional Types下面的例子中得到这个想法

    所以我已经对其进行了调整,并且它有效:

    interface SOME_OBJECT {
        title:      string,
        label:      string,
        someBool:   boolean,
        someDate:   Date,
        someNumber: number
    }
    
    type ExtractStringPropertyNames<T> = {
        [K in keyof T]: T[K] extends string ? K : never
    }[keyof T]
    
    type STRING_KEYS = ExtractStringPropertyNames<SOME_OBJECT>
    

    Typescript playground

    如果可能的话,我仍然有兴趣查看其他方法。特别是如果有更直接的方法,因为我认为这个答案感觉像是“黑客”,因为它并没有很清楚代码在做什么。


    更新(如何运作)

    为了了解代码实际在做什么,我进行了更多研究,我想我已经弄明白了。

    我把它分为两个步骤:

    第 1 步

    在第一步中,将创建一个新的对象/类型。

    此新对象的键将与通用类型 T(在示例中为 SOME_OBJECT)的键相同的键 K in keyof T

    对于这些键的值,我们将检查泛型类型T,如果这些属性的值K 扩展string。这意味着这些值可以分配给string。例如:string"some_hard_coded_string_type"any 都会通过测试。

    对于每个通过测试的属性,我们将重复其键 K 作为它们的值,否则将通过 never。您可以在下面的STEP_1_RESULT 中看到结果。

    第 2 步

    在这一步中,我们将简单地获取从 步骤 1 生成的对象,然后询问 Typescript 通过执行type STEP_2&lt;T&gt; = T[keyof T] 来返回其所有可能的值。

    请记住,keyof T 代表来自T 的所有属性的联合。因此,当与 keyof T 联合的成员一起调用时,Typescript 将返回一个联合,其中包含 STEP_1_RESULT 对象的所有可能值。

    在我们的示例中,它将返回 "title" | "label" | never | never | never

    但由于 never 类型在联合类型中毫无意义,因此这些值将被丢弃,而我们只剩下 "title" | "label"

    【讨论】:

    • 好主意。在我看来并不老套,只是冗长。
    • @CertainPerformance,谢谢。你是对的。现在我已经更详细地研究了它的工作原理,对我来说感觉不那么奇怪了。我想我只是不习惯看到这种模式。
    • 您也可以一步创建类型。以防您用完了类型名称。 type ExtractStringPropertyNames = { [K in keyof T]: T[K] 扩展字符串? K : 从不 }[keyof T];
    【解决方案2】:

    我不太确定条件类型。我可能会在这里尝试使用Pick,我会做类似的事情

    interface SOME_OBJECT {
        title:      string,
        label:      string,
        someBool:   boolean,
        someDate:   Date,
        someNumber: number
    }
    
    type ExtractString = Pick<SOME_OBJECT, "title" | "label">;
    
    type STRING_KEYS = ExtractString
    

    【讨论】:

    • 谢谢,乔治斯。您的答案适用于这个特定的例子。但我需要一种通用的方法。适用于任何可能出现的具有任意数量的属性和属性名称的对象。
    猜你喜欢
    • 2020-12-03
    • 2011-11-05
    • 1970-01-01
    • 1970-01-01
    • 2020-09-18
    • 2019-02-13
    • 1970-01-01
    • 1970-01-01
    • 2018-12-13
    相关资源
    最近更新 更多