【问题标题】:Removing/filtering properties of specific type from another [duplicate]从另一个[重复]中删除/过滤特定类型的属性
【发布时间】:2021-04-04 06:36:51
【问题描述】:

我想做类似以下的事情。考虑我们有一个类型:

interface MyInterface {
    stringProp: string,
    numberProp: number
}

我想创建一个实用程序类型,它会在给定另一种类型(例如numberstring)的情况下过滤掉该类型的属性,如下所示:

/// Utility type, this is the thing I'm trying to define
type TypeFiltered<T extends object, V> = ...

// Apply to MyInterface, number properties only
type MyInterfaceNumberProperties = TypeFiltered<MyInterface, number>

// MyInterfaceNumberProperties is
// { numberProp: number }

我知道我可以使用 never 获取我不想要的类型的完整结构:

type ReplaceWithNever<T extends object, V> = {
    [K in keyof T]: T[K] extends V ? T[K] : never
}

但这就是我卡住的地方,至于MyInterface,它有效地产生了一个具有以下内容的类型:

/// ReplaceWithNever<MyInterface, number>
{
    myString: never,
    myNumber: number
}

有没有办法从keyof ReplaceWithNever&lt;T, V&gt; 中过滤掉keyof ReplaceWithNever&lt;T, V&gt; 产生never 的项目?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    简短的回答是这样的:

    type TypeFiltered < T extends object, V > = Pick < T, { [ P in keyof T ]: T[P] extends V ? P : never }[ keyof T ] >
    
    type T = TypeFiltered < MyInterface, number > // { numberProp: number }
    

    更长的答案是,它的构建如下(参见playground):

    首先,通过为具有与给定对象相同的属性名称的对象形成一个类型,但是属性类型是(i)给定对象的属性名称(当它们是指定类型时),或者( ii)never。这是在新对象中使用带有索引签名的条件类型:

    type PropertyNamesOfTypeOrNever < T extends object, V > = { [ P in keyof T ]: T[P] extends V ? P : never }
    
    type X = PropertyNamesOfTypeOrNever < MyInterface, number > // { stringProp: never, numberProp: "numberProp" }
    

    其次,使用索引访问运算符,从属性名称的联合类型中,其中属性值具有特定类型:

    type PropertyNamesFiltered < T extends object, V > = PropertyNamesOfTypeOrNever < T, V >[ keyof T ]
    
    type Y = PropertyNamesFiltered < MyInterface, number > // "numberProp"
    

    最后,使用该联合使用内置的Pick 类型进行过滤:

    type TypeFilteredComposite < T extends object, V > = Pick < T, PropertyNamesFiltered < T, V > >
    
    type Z = TypeFilteredComposite < MyInterface, number > // { numberProp: number }
    

    【讨论】:

    • 操场的链接包含操场说无效的代码。
    • 已更正。感谢您的观看。
    【解决方案2】:

    在 TypeScript 4.1 中,您可以使用 key remapping in mapped types(使用 as 子句)通过将这些键重新映射到 never 来过滤掉属性:

    // TS4.1+
    type TypeFiltered<T extends object, V> = {
        [K in keyof T as T[K] extends V ? K : never]: T[K]
    }
    

    在 TypeScript 4.0 及以下版本中,您需要使用类似 Pick utility type 的东西来过滤属性,您需要通过 key 而不是 value 来识别这些属性。 ..强迫你做一个更复杂的操作:

    type TypeFilteredOld<T extends object, V> = Pick<T, {
        [K in keyof T]-?: T[K] extends V ? K : never }[keyof T]
    >;
    

    无论哪种方式都应该产生相同的类型:

    type MyInterfaceNumberProperties = TypeFiltered<MyInterface, number>
    /* type MyInterfaceNumberProperties = {
        numberProp: number;
    } */
    

    Playground link to code

    【讨论】:

      猜你喜欢
      • 2018-11-23
      • 1970-01-01
      • 1970-01-01
      • 2015-08-31
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多