Typescript如何创建具有两种常见属性的类型?

Kir*_*l A 11 typescript

有两种类型

type A = {
  x: number
  y: number
}

type B = {
  y: number
  z: number
}
Run Code Online (Sandbox Code Playgroud)

如何获得具有该类型的共同属性的类型?

type C = Something<T1, T2> // { y: number }
Run Code Online (Sandbox Code Playgroud)

qiu*_*qiu 15

天真的方法的缺点

虽然其他答案中通常建议使用以下类型:

type Common<A, B> = {
    [P in keyof A & keyof B]: A[P] | B[P];
}
Run Code Online (Sandbox Code Playgroud)

它无法检查属性值是否可以相互分配。这意味着和可能Common<A, B>具有并非真正共享的属性。AB

type A = { a: number; x: string; z: number }
type B = { b: number; x: number; z: number}
Common<A, B> // => { x: string | number; z: number}
// above should just be { z: number }, since the type of property x is not
// assignable to the type of property x in both A and B.
Run Code Online (Sandbox Code Playgroud)

这很糟糕的原因是因为将 type 的对象解构为 type或 typeCommon<A, B> 的对象对于或可能会失败。例如,ABAB

const sharedProps: Common<A, B> = { x: 'asdf', z: 9 }
const a: A = { ...sharedProps, a: 1 }

// below throws type error; string not assignable to number
const b: B = { ...sharedProps, b: 1 }
Run Code Online (Sandbox Code Playgroud)

这很令人困惑,并且对我们可以使用泛型类型执行的操作施加了任意限制。

涵盖上述用例的深入方法

我建议使用以下类型(适用于 TS4.1+):

/**
 * Omits properties that have type `never`. Utilizes key-remapping introduced in
 * TS4.1.
 *
 * @example
 * ```ts
 * type A = { x: never; y: string; }
 * OmitNever<A> // => { y: string; }
 * ```
 */
type OmitNever<T extends Record<string, unknown>> = {
  [K in keyof T as T[K] extends never ? never : K]: T[K];
};

/**
 * Constructs a Record type that only includes shared properties between `A` and
 * `B`. If the value of a key is different in `A` and `B`, `SharedProperties<A,
 * B>` attempts to choose a type that is assignable to the types of both values.
 *
 * Note that this is NOT equivalent to `A & B`.
 *
 * @example
 * ```ts
 * type A = { x: string; y: string; }
 * type B = { y: string; z: string }
 * type C = { y: string | number; }
 *
 * A & B                  // => { x: string; y: string; z: string; }
 * SharedProperties<A, B> // => { y: string; }
 * SharedProperties<B, C> // => { y: string | number; }
 * ```
 */
type SharedProperties<A, B> = OmitNever<Pick<A & B, keyof A & keyof B>>;
Run Code Online (Sandbox Code Playgroud)

此类型正确返回保证是 和 的子类型的共享属性AB因为只要不是,A & B就保证是A和 的子类型。BA & Bnever

type A = { a: number; x: string; z: number }
type B = { b: number; x: number; z: number}
SharedProperties<A, B> // => { z: number }
Run Code Online (Sandbox Code Playgroud)


kub*_*ube 13

共同属性

使用静态keyof运算符:

type Ka = keyof A // 'x' | 'y'
type Kb = keyof B // 'y' | 'z'
type Kc = Ka & Kb // 'y'
Run Code Online (Sandbox Code Playgroud)

并使用以下属性定义映射类型Kc:

type C = {
  [K in keyof A & keyof B]: A[K] | B[K]
}
Run Code Online (Sandbox Code Playgroud)

这定义了一种新类型,其中每个键都将出现在A和中B.

与此键关联的每个值都将具有类型A[K] | B[K],以防A[K]B[K]不同.


仅具有相同类型的公共属性

仅当在A和B中键入相同时,才使用条件类型将键映射到值:

type MappedC = {
  [K in keyof A & keyof B]:
    A[K] extends B[K] // Basic check for simplicity here.
    ? K // Value becomes same as key
    : never // Or `never` if check did not pass
}
Run Code Online (Sandbox Code Playgroud)

通过访问所有键,从此对象获取所有值的并集:

// `never` will not appear in the union
type Kc = MappedC[keyof A & keyof B]
Run Code Online (Sandbox Code Playgroud)

最后:

type C = {
  [K in Kc]: A[K]
}
Run Code Online (Sandbox Code Playgroud)


Fab*_*uer 10

根据@kube 的回答,您可以使用泛型来创建可重用类型:

type Common<A, B> = {
    [P in keyof A & keyof B]: A[P] | B[P];
}
Run Code Online (Sandbox Code Playgroud)

这允许您即时创建交叉点:

const c: Common<T1, T2> = { y: 123 };
Run Code Online (Sandbox Code Playgroud)