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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41476063/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 04:08:18  来源:igfitidea点击:

TypeScript: remove key from type/subtraction type

reactjsgenericstypescriptdecoratorhigher-order-components

提问by Leigh Brenecki

I want to define a generic type ExcludeCart<T>that is essentially Tbut with a given key (in my case, cart) removed. So, for instance, ExcludeCart<{foo: number, bar: string, cart: number}>would be {foo: number, bar: string}. Is there a way to do this in TypeScript?

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

Here's why I want to do this, in case I'm barking up the wrong tree: I'm converting an existing JavaScript codebase to TypeScript, which contains a decorator function called cartifythat takes a React component class Innerand returns another component class Wrapper.

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

Innershould take a cartprop, and zero or more other props. Wrapperaccepts a cartClientprop (which is used to generate the cartprop to pass to Inner), and any prop that Inneraccepts, exceptcart.

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

In other words, once I can figure out how to define ExcludeCart, I want to do this with it:

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

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

采纳答案by Adrian Leonhard

While there isn't a built-in subtraction type, you can currently hack it in:

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

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]>

In the question's case, you would do:

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

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

With TypeScript >= 2.6, you can simplify it to:

使用 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>>

test it on the playground

在操场上测试一下

回答by alter igel

Since TypeScript 2.8and the introduction of Exclude, It's now possible to write this as follows:

TypeScript 2.8和 的引入开始Exclude,现在可以这样写:

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

Or alternatively, and more concisely, as:

或者,更简洁地说,如:

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

For your usage, you could now write the following:

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

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

回答by Mathias

While this has been correctly answered, I wanted to point out that TypeScript 3.5did add an Omit<T, E>type.

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

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

This results in the {foo: string, bar: string}type.

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

回答by Félix Brunet

there is another very simple way to have this result

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

When combining type in typescript, the type "never" have higher priority to everything.

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

You can simply create a type:

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

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

Or, without creating type

或者,不创建类型

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

This is less generic than the solution of Adrian, but a bit simpler when we don't need the complexity.

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

回答by James McMahon

So, for instance, ExcludeCart<{foo: number, bar: string, cart: number}> would be {foo: number, bar: string}

因此,例如, ExcludeCart<{foo: number, bar: string, cart: number}> 将是 {foo: number, bar: string}

You can use the Excludesyntax to do this directly:

您可以使用Exclude语法直接执行此操作:

Exclude<{foo: number, bar: string, cart: number}, { cart: number}>

回答by JKillian

Update: See Adrian's answer abovefor a solution to this question. I've left my answer here though since it still contains some useful links.

更新:有关此问题的解决方案,请参阅上面阿德里安的回答。我已经把我的答案留在这里了,因为它仍然包含一些有用的链接。



There are various old requests for this feature ("outersection" types, subtraction types), but none have really progressed.

对此功能有各种旧要求(“外部”类型减法类型),但没有一个真正取得进展。

Recently, with the addition of mapped types I asked about this again, and Anders said that while there's no plans to make a general subtraction type operator, a more limited version might be implemented, presumably looking something like this proposal.

最近,随着映射类型的添加,我再次询问了这个问题,Anders 说虽然没有计划制作通用减法类型 operator,但可能会实现一个更有限的版本,大概看起来像这个提案

I've personally run into quite similar situations to you when working with React, and unfortunately haven't been able to find any good solution. In a simple case, you can get away with something like:

在使用 React 时,我个人遇到了与您非常相似的情况,不幸的是,还没有找到任何好的解决方案。在一个简单的情况下,您可以使用以下方法:

interface BaseProps {
    foo: number;
    bar: number;
}

interface Inner extends BaseProps {
    cart: Cart;
}

interface Wrapper extends BaseProps {
    cartClient: Client;
}

but I almost find this to be a semantic abuse of the extendskeyword. And of course, if you don't control the typings of Inneror BaseProps, then this won't work out.

但我几乎发现这是对extends关键字的语义滥用。当然,如果您不控制Inneror 的类型BaseProps,那么这将行不通。