Typescript:省略嵌套属性

Joh*_*auß 18 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
}
Run Code Online (Sandbox Code Playgroud)

我想为我的表单创建一个类型,需要在界面中省略attributeValues.optionalAttributes和。attributeValues.mandatoryAttributes

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

export type CampaignFormValues = Omit<Campaign, 'attributeValues.mandatoryAttributes'>
Run Code Online (Sandbox Code Playgroud)

但这行不通。

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

export type CampaignFormValues = Omit<Campaign, 'optionalAttributes'>
Run Code Online (Sandbox Code Playgroud)

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

有没有办法用 Typescript 进行嵌套省略?

han*_*aad 9

首先,省略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 }
  }
}

Run Code Online (Sandbox Code Playgroud)

操场


cap*_*ian 6

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


Run Code Online (Sandbox Code Playgroud)

操场

更多解释你可以在我的博客中找到

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

如果您想使用点语法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
Run Code Online (Sandbox Code Playgroud)