TypeScript:从 type/subtraction 类型中删除键
TypeScript: remove key from type/subtraction type
我想定义一个泛型类型 ExcludeCart<T>
,它本质上是 T
,但删除了给定的键(在我的例子中是 cart
)。因此,例如,ExcludeCart<{foo: number, bar: string, cart: number}>
将是 {foo: number, bar: string}
。有没有办法在 TypeScript 中做到这一点?
这就是我要这样做的原因,以防我找错树了:我正在将现有的 JavaScript 代码库转换为 TypeScript,其中包含一个名为 cartify
的装饰器函数需要一个 React 组件 class Inner
和 returns 另一个组件 class Wrapper
.
Inner
应该采用 cart
道具,以及零个或多个其他道具。 Wrapper
接受一个 cartClient
道具(用于生成要传递给 Inner
的 cart
道具),以及 Inner
接受的任何道具,除了 cart
.
换句话说,一旦我弄清楚如何定义 ExcludeCart
,我想用它来做:
function cartify<P extends {cart: any}>(Inner: ComponentClass<P>) : ComponentClass<ExcludeCart<P> & {cartClient: any}>
更新:请参阅 了解此问题的解决方案。我在这里留下了我的答案,因为它仍然包含一些有用的链接。
对此功能有各种旧要求 ("outersection" types, subtraction types),但 none 确实取得了进展。
最近,由于添加了映射类型,我再次询问了这个问题,Anders 说 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。
我个人 运行 在使用 React 时遇到过与您非常相似的情况,但遗憾的是未能找到任何好的解决方案。在一个简单的例子中,你可以逃避这样的事情:
interface BaseProps {
foo: number;
bar: number;
}
interface Inner extends BaseProps {
cart: Cart;
}
interface Wrapper extends BaseProps {
cartClient: Client;
}
但我几乎认为这是对 extends
关键字的语义滥用。当然,如果您不控制 Inner
或 BaseProps
的输入,那么这将无法解决。
虽然没有内置的减法类型,但您目前可以破解它:
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 3.5 更新:Omit<Type, Keys>
utility type is now available. Please see 示例用法。
旧答案:从TypeScript 2.8 and the introduction of 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 中组合类型时,类型 "never" 具有更高的优先级。
您可以简单地创建一个类型:
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 的解决方案那么通用,但在我们不需要复杂性时会更简单一些。
So, for instance, ExcludeCart<{foo: number, bar: string, cart: number}> would be {foo: number, bar: string}
您可以使用 Exclude 语法直接执行此操作:
Exclude<{foo: number, bar: string, cart: number}, { cart: number}>
虽然回答正确,但我想指出 TypeScript 3.5 did add an Omit<T, E>
类型。
type NoCart = Omit<{foo: string, bar: string, cart: number}, "cart">;
这导致 {foo: string, bar: string}
类型。
我想定义一个泛型类型 ExcludeCart<T>
,它本质上是 T
,但删除了给定的键(在我的例子中是 cart
)。因此,例如,ExcludeCart<{foo: number, bar: string, cart: number}>
将是 {foo: number, bar: string}
。有没有办法在 TypeScript 中做到这一点?
这就是我要这样做的原因,以防我找错树了:我正在将现有的 JavaScript 代码库转换为 TypeScript,其中包含一个名为 cartify
的装饰器函数需要一个 React 组件 class Inner
和 returns 另一个组件 class Wrapper
.
Inner
应该采用 cart
道具,以及零个或多个其他道具。 Wrapper
接受一个 cartClient
道具(用于生成要传递给 Inner
的 cart
道具),以及 Inner
接受的任何道具,除了 cart
.
换句话说,一旦我弄清楚如何定义 ExcludeCart
,我想用它来做:
function cartify<P extends {cart: any}>(Inner: ComponentClass<P>) : ComponentClass<ExcludeCart<P> & {cartClient: any}>
更新:请参阅
对此功能有各种旧要求 ("outersection" types, subtraction types),但 none 确实取得了进展。
最近,由于添加了映射类型,我再次询问了这个问题,Anders 说 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。
我个人 运行 在使用 React 时遇到过与您非常相似的情况,但遗憾的是未能找到任何好的解决方案。在一个简单的例子中,你可以逃避这样的事情:
interface BaseProps {
foo: number;
bar: number;
}
interface Inner extends BaseProps {
cart: Cart;
}
interface Wrapper extends BaseProps {
cartClient: Client;
}
但我几乎认为这是对 extends
关键字的语义滥用。当然,如果您不控制 Inner
或 BaseProps
的输入,那么这将无法解决。
虽然没有内置的减法类型,但您目前可以破解它:
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 3.5 更新:Omit<Type, Keys>
utility type is now available. Please see
旧答案:从TypeScript 2.8 and the introduction of 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 中组合类型时,类型 "never" 具有更高的优先级。
您可以简单地创建一个类型:
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 的解决方案那么通用,但在我们不需要复杂性时会更简单一些。
So, for instance, ExcludeCart<{foo: number, bar: string, cart: number}> would be {foo: number, bar: string}
您可以使用 Exclude 语法直接执行此操作:
Exclude<{foo: number, bar: string, cart: number}, { cart: number}>
虽然回答正确,但我想指出 TypeScript 3.5 did add an Omit<T, E>
类型。
type NoCart = Omit<{foo: string, bar: string, cart: number}, "cart">;
这导致 {foo: string, bar: string}
类型。