semlinker/awesome-typescript

「重学TS 2.0 」TS 练习题第二十一题

semlinker opened this issue · 15 comments

实现一个 Merge 工具类型,用于把两个类型合并成一个新的类型。第二种类型(SecondType)的 Keys 将会覆盖第一种类型(FirstType)的 Keys。具体的使用示例如下所示:

type Foo = { 
   a: number;
   b: string;
};

type Bar = {
   b: number;
};

type Merge<FirstType, SecondType> = // 你的实现代码

const ab: Merge<Foo, Bar> = { a: 1, b: 2 };

请在下面评论你的答案。

type Foo = {
  a: number;
  b: string;
};

type Bar = {
  b: number;
};

type Merge<FirstType, SecondType> = {
  [K in keyof (FirstType & SecondType)]: K extends keyof SecondType
    ? SecondType[K]
    : K extends keyof FirstType
    ? FirstType[K]
    : never;
};

const ab: Merge<Foo, Bar> = { a: 1, b: 2 };
type Foo = {
  a: number;
  b: string;
};

type Bar = {
  b: number;
};

type Merge<FirstType, SecondType> = {
  [T in keyof (FirstType & SecondType)]: T extends keyof SecondType ? SecondType[T] : T extends keyof FirstType ? FirstType[T] : never;
};

const ab: Merge<Foo, Bar> = { a: 1, b: 1 };

ppbl commented
type Merge<FirstType, SecondType> = Omit<FirstType, keyof SecondType> & SecondType

题目分析

本题目的是实现一个 Merge 工具类型,将两个类型合并成一个新的类型。并且后一个类型覆盖前一个类型。

解法1

  • 将 FirstType 和 SecondType 做交叉类型,并遍历每一个属性;
  • 如果当前属性名在 SecondType 类型中,则使用 SecondType 类型中的当前属性的值;
  • 如果当前属性名在 FirstType 类型中,则使用 FirstType 类型中的当前属性的值;
  • 否则为 never;

这边先判断属性是否在 SecondType 类型,是因为需要优先使用 SecondType 类型的值。

type Merge <FirstType, SecondType> = {
    [K in keyof (FirstType & SecondType)]:
    K extends keyof SecondType ?
    SecondType[K] :
    K extends keyof FirstType ?
    FirstType[K] :
    never
}
const ab: Merge<Foo, Bar> = { a: 1, b: 2 };

解法2

  • 先将 FirstType 类型中已有的,和 SecondType 类型中相同属性删除;
  • 将前面结果和 SecondType 做交叉类型,获得合并后结果。

这样就能确保 SecondType 类型的值能覆盖 FirstType 类型中相同的属性。

type Merge <FirstType, SecondType> = Omit<FirstType, keyof SecondType> & SecondType;

const ab: Merge<Foo, Bar> = { a: 1, b: 2 };

解法3

  • 先获取 FirstType 类型和 SecondType 类型的交集类型;
  • 定义一个新类型 ExtractType,内容为前面的交集类型,交集类型中每个属性的值为 SecondType 类型中该属性的值;
  • 然后将 FirstType 类型中已有的,和 SecondType 类型中相同属性删除;
  • 将删除后的类型和 ExtractType 类型做交叉类型,获得合并后结果。

主要是先取出两个类型的交集,再和第一个类型做交叉操作。

type ExtractType<FirstType, SecondType> = {
    [K in Extract<keyof FirstType, keyof SecondType>]: SecondType[K]
};

type Merge <FirstType, SecondType> = Omit<FirstType, keyof SecondType> & ExtractType<FirstType, SecondType>;

const ab: Merge<Foo, Bar> = { a: 1, b: 2 };
type Foo = { 
   a: number;
   b: string;
   c: boolean
};

type Bar = {
   b: boolean;
   a?: string;
   d?: number
};
type PickAll<T> = {
  [p in keyof T]: T[p]
}
type Merge<FirstType, SecondType> = PickAll<Omit<FirstType, keyof SecondType> & SecondType>// 你的实现代码

const ab: Merge<Foo, Bar> = { b: true, c: false };
type Foo = {
	a: number;
	b: string;
};

type Bar = {
	b: number;
};

type Merge<FirstType, SecondType> = {
  // [T in keyof (FirstType & SecondType)] 写法返回的结果是{},没有报错,看似通过,实际不合题意
  [T in (keyof FirstType | keyof SecondType)]: T extends keyof SecondType ? SecondType[T] : T extends keyof FirstType ? FirstType[T] : never;
}

const ab: Merge<Foo, Bar> = { a: 1, b: 2 };
ln0y commented
  type Foo = {
    a: number
    b: string
  }

  type Bar = {
    b: number
  }

  type Merge<FirstType, SecondType> = {
    [P in keyof Omit<FirstType, keyof SecondType>]: FirstType[P]
  } & SecondType

  type T1 = Simplify<Merge<Foo, Bar>>

  const ab: Merge<Foo, Bar> = { a: 1, b: 2 }
// 实现一个 Merge 工具类型,用于把两个类型合并成一个新的类型。第二种类型(SecondType)的 Keys 将会覆盖第一种类型(FirstType)的 Keys。具体的使用示例如下所示:

type Foo = {
   a: number;
   b: string;
};

type Bar = {
   b: number;
};

type Simplify<T> = { [k in keyof T]: T[k]; }
type Merge<FirstType, SecondType> = Simplify<Omit<FirstType, keyof SecondType> & SecondType>;

type T1 = Merge<Foo, Bar>
const ab: Merge<Foo, Bar> = { a: 1, b: 2 };

解题思路: 其实就是利用交叉类型 & 来实现,不过要去除掉第二个类型里的属性,这样确保交叉的时候相同的属性以第二个类型为准

Mrlgm commented
type Foo = {
    a: number;
    b: string;
};

type Bar = {
    b: number;
};

type Merge<FirstType, SecondType> = {
    [K in keyof (FirstType & SecondType)]: K extends keyof SecondType ? SecondType[K] : K extends keyof FirstType ? FirstType[K] : never

}

const ab: Merge<Foo, Bar> = { a: 1, b: 2 };
type MergeType<T extends {[k: string]: any} = {}, U extends {[k: string]: any} = {}> = {
  [k in (keyof T | keyof U)]: k extends keyof U ? U[k] : k extends keyof T ? T[k] : never
}
type Foo = { 
  a: number;
  b: string;
};

type Bar = {
  b: number;
};

const ab: MergeType<Foo, Bar> = { a: 1, b: 2 };

export default {}

// 实现一个 Merge 工具类型,用于把两个类型合并成一个新的类型。第二种类型(SecondType)的 Keys 将会覆盖第一种类型(FirstType)的 Keys。具体的使用示例如下所示:
type Foo = {
a: number;
b: string;
};

type Bar = {
b: number;
};

type test = keyof ({a: string} & {a: number})

type Merge<A, B> = {
[P in keyof (A & B)]: P extends keyof B ? B[P] : P extends keyof A ? A[P] : never
}

const ab: Merge<Foo, Bar> = { a: 1, b: 2 };

zouyk commented
type GetKeys<T extends {}> = keyof T 

type Merge<FirstType, SecondType> = GetKeys<SecondType> extends  GetKeys<FirstType> ? {
    [K in GetKeys<FirstType>] : K extends GetKeys<SecondType> ? SecondType[K] : FirstType[K]
}:
GetKeys<FirstType> extends  GetKeys<SecondType> ? 
{
    [K in GetKeys<SecondType>] : K extends GetKeys<FirstType> ? FirstType[K] : SecondType[K]
}:
FirstType & SecondType
type Foo = { 
   a: number;
   b: string;
};

type Bar = {
   b: number;
};
const ab: Merge2<Foo, Bar> = { a: 1, b: 2 };

type FirstKey<FirstType, SecondType> = Exclude<keyof FirstType, keyof SecondType>

type Merge<FirstType, SecondType> = {
  [K in FirstKey<FirstType, SecondType>]: FirstType[K]
} & SecondType

type Merge2<FirstType, SecondType> = Omit<FirstType, keyof SecondType> & SecondType

题主你第三种方式,只保留了第一个类型的属性已经将第一个类型与第二个类型相同属性的值的改变,并没有将第二个类型额外的属性添加到新的类型中.

type Merge<FirstType, SecondType> = {
    [K in keyof (FirstType & SecondType)]: K extends infer R ? (R extends keyof FirstType ? (R extends keyof SecondType ? SecondType[R] : FirstType[R]) : (R extends keyof SecondType ? SecondType[R] : never)) : never
}