打字稿:省略嵌套属性

时间:2021-06-08 09:11:42

标签: typescript

我有一个这样的界面:

export interface Campaign {
  id: string
  orders?: number
  avgOrderValue?: number
  optionalAttributes: string[]
  attributeValues: {
    optionalAttributes: CampaignAttribute[]
    mandatoryAttributes: CampaignAttribute[]
    values?: { [key: string]: unknown }
  }
  created: number
  lastUpdated: number
}

我想为我的表单创建一个类型,需要从界面中省略 attributeValues.optionalAttributesattributeValues.mandatoryAttributes

我在想也许 Typescript 可以做这样的事情:

export type CampaignFormValues = Omit<Campaign, 'attributeValues.mandatoryAttributes'>

但这不起作用。

我使用了这个问题的答案:Deep Omit with typescript 但是这个答案只是深深地省略了每个匹配的键,所以像这样使用它:

export type CampaignFormValues = Omit<Campaign, 'optionalAttributes'>

还会删除我想保留的根级别 optionalAttributes

有没有办法用 Typescript 做嵌套省略?

3 个答案:

答案 0 :(得分:1)

type A = {
    a: {
        b: string
        c: string
    }
    x: {
        y: number
        z: number,
        w: {
            u: number
        }
    }
}
type Primitives = string | number | boolean | symbol

/**
 * Get all valid nested pathes of object
 */
type AllProps<Obj, Cache extends Array<Primitives> = []> =
    Obj extends Primitives ? Cache : {
        [Prop in keyof Obj]:
        | [...Cache, Prop] // <------ it should be unionized with recursion call
        | AllProps<Obj[Prop], [...Cache, Prop]>
    }[keyof Obj]

type Head<T extends ReadonlyArray<any>> =
    T extends []
    ? never
    : T extends [infer Head]
    ? Head
    : T extends [infer Head, ...infer _]
    ? Head
    : never


type Tail<T extends ReadonlyArray<any>> =
    T extends []
    ? []
    : T extends [infer _]
    ? []
    : T extends [infer _, ...infer Rest]
    ? Rest
    : never

type Last<T extends ReadonlyArray<any>> = T['length'] extends 1 ? true : false


type OmitBase<Obj, Path extends ReadonlyArray<any>> =
    Last<Path> extends true
    ? {
        [Prop in Exclude<keyof Obj, Head<Path>>]: Obj[Prop]
    } : {
        [Prop in keyof Obj]: OmitBase<Obj[Prop], Tail<Path>>
    }

// we should allow only existing properties in right order
type OmitBy<Obj, Keys extends AllProps<Obj>> = OmitBase<A, Keys>

type Result = OmitBy<A,['a', 'b']> // ok

type Result2 = OmitBy<A,['b']> // expected error. order should be preserved


Playground

More explanation 你可以在我的博客中找到

以上解决方案适用于深层嵌套类型

如果您想使用点语法 prop1.prop2,请考虑下一种类型:

type Split<Str, Cache extends string[] = []> =
    Str extends `${infer Method}.${infer Rest}`
    ? Split<Rest, [...Cache, Method]>
    : Str extends `${infer Last}`
    ? [...Cache, Last,]
    : never
    
type WithDots = OmitBy<A, Split<'a.b'>> // ok

答案 1 :(得分:0)

您需要创建一个覆盖 attributeValues 的新界面:

export interface Campaign {
  id: string
  orders?: number
  avgOrderValue?: number
  optionalAttributes: string[]
  attributeValues: {
    optionalAttributes: CampaignAttribute[]
    mandatoryAttributes: CampaignAttribute[]
    values?: { [key: string]: unknown }
  }
  created: number
  lastUpdated: number
}

interface MyOtherCampaign extends Omit<Campaign, 'attributeValues'> {
    attributeValues: {
      values?: { [key: string]: unknown }
    }
}

let x:MyOtherCampaign;

enter image description here

Playground

答案 2 :(得分:0)

首先,省略 attributeValues,然后将其添加回来并删除属性。

export interface Campaign {
  id: string
  attributeValues: {
    optionalAttributes: string[]
    mandatoryAttributes: string[]
    values?: { [key: string]: unknown }
  }
}

type ChangeFields<T, R> = Omit<T, keyof R> & R;
type CampaignForm = ChangeFields<Campaign, { 
  attributeValues: Omit<Campaign['attributeValues'], 'mandatoryAttributes'|'optionalAttributes'> 
}>;

const form: CampaignForm = {
  id: '123',
  attributeValues: {
    values: { '1': 1 }
  }
}

Playground