const merge = <T, U extends T>(obj1: T, obj2: U): T & U => ({
  ...obj1,
  ...obj2,
});

override({ a: 1 }, { a: 24, b: 8 });
// override({ a: 2 }, { x: 73 });  /* compile error */

<T, U extends T>

「ここでの extends は、関数 override() の第 2 引数 obj2 の型を定義している型引数 U が第 1 引数 の型 obj1 の型 T と同じか拡張したものでなければならないことを示唆するもの。だからその条件 に従わない引数を渡そうとするとコンパイルで弾かれるの」

<条件付き型>

TextendsU?X:Y

型 T が 型 U を拡張していた場合は型 X を、それ以外の場合は型 Y となる型の記述

type User = { id: unknown };
type NewUser = User & { id: string }; 
type OldUser = User & { id: number }; 
type Book = { isbn: string };

type IdOf<T> = T extends User ? T['id'] : never;

type NewUserId = IdOf<NewUser>; //string 
type OldUserId = IdOf<OldUser>; //number
type BookId = IdOf<Book>; // never