TypeScript:从类型/减法类型中删除键

IT技术 reactjs generics typescript decorator higher-order-components
2021-05-03 12:56:45

我想定义一个泛型类型ExcludeCart<T>,该类型本质T上是cart删除了给定的键(在我的情况下,)。因此,例如,ExcludeCart<{foo: number, bar: string, cart: number}>将是{foo: number, bar: string}有没有办法在 TypeScript 中做到这一点?

这就是我想要这样做的原因,以防万一我找错了树:我将现有的 JavaScript 代码库转换为 TypeScript,它包含一个称为装饰器函数的函数,该函数cartify接受一个 React 组件类Inner并返回另一个组件类Wrapper

Inner应该带一个cartprops,以及零个或多个其他props。Wrapper接受一个cartClientprops(用于生成cart要传递给propsInner),以及任何Inner接受的props除了 cart.

换句话说,一旦我能弄清楚如何定义ExcludeCart,我就想用它来做到这一点:

function cartify<P extends {cart: any}>(Inner: ComponentClass<P>) : ComponentClass<ExcludeCart<P> & {cartClient: any}>
4个回答

TypeScript 3.5 更新Omit<Type, Keys>实用程序类型现在可用。有关示例用法,请参阅Mathias 的回答


旧答案:自从TypeScript 2.8和 的引入Exclude,现在可以这样写:

type Without<T, K> = {
    [L in Exclude<keyof T, K>]: T[L]
};

或者,更简洁地说,如:

type Without<T, K> = Pick<T, Exclude<keyof T, K>>;

为了您的使用,您现在可以编写以下内容:

type ExcludeCart<T> = Without<T, "cart">;

虽然这已经得到正确回答,但我想指出TypeScript 3.5确实添加了一个Omit<T, E>类型。

type NoCart = Omit<{foo: string, bar: string, cart: number}, "cart">;

这导致{foo: string, bar: string}类型。

虽然没有内置的减法类型,但您目前可以将其破解:

type Sub0<
    O extends string,
    D extends string,
> = {[K in O]: (Record<D, never> & Record<string, K>)[K]}

type Sub<
    O extends string,
    D extends string,
    // issue 16018
    Foo extends Sub0<O, D> = Sub0<O, D>
> = Foo[O]

type Omit<
    O,
    D extends string,
    // issue 16018
    Foo extends Sub0<keyof O, D> = Sub0<keyof O, D>
> = Pick<O, Foo[keyof O]>

在问题的情况下,你会这样做:

type ExcludeCart<T> = Omit<T, 'cart'>

使用 TypeScript >= 2.6,您可以将其简化为:

/**
 * for literal unions
 * @example Sub<'Y' | 'X', 'X'> // === 'Y'
 */
export type Sub<
    O extends string,
    D extends string
    > = {[K in O]: (Record<D, never> & Record<string, K>)[K]}[O]

/**
 * Remove the keys represented by the string union type D from the object type O.
 *
 * @example Omit<{a: number, b: string}, 'a'> // === {b: string}
 * @example Omit<{a: number, b: string}, keyof {a: number}> // === {b: string}
 */
export type Omit<O, D extends string> = Pick<O, Sub<keyof O, D>>

在操场上测试一下

还有另一种非常简单的方法可以得到这个结果

在typescript中组合类型时,“从不”类型对所有内容都有更高的优先级。

您可以简单地创建一个类型:

type noCart<T> = T & {cart : never}

或者,不创建类型

function removeCart<T>(obj : T) : T & {cart : never} {
    if("cart" in obj) {
        delete (obj as T & {cart : any}).cart;
    }
    return <T & {cart : never}> obj;
}

这不像 Adrian 的解决方案那么通用,但是当我们不需要复杂性时会更简单一些。