extends здесь значит «assignable to», не наследование.<T extends string> принимает string, string-literal types и их unions — всё structurally assignable к string.
Constraint ограничивает вход, но не расширяет inferred type.function id<T extends object>(x: T): T на { a: 1 } вернёт { a: 1 }, неobject. Constraint решает, кто проходит; inference сохраняет точный тип аргумента.
Рабочая лошадка — K extends keyof T:
function get<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
K — реальный ключ T, return — indexed access T[K]: get(user, 'name') имеет тип user.name, get(user, 'nope') — ошибка.
Defaults срабатывают только когда inference молчит. Любой аргумент, фиксирующий T, игнорирует default. Defaults могут ссылаться на ранние параметры: <T, U extends T = T>.
Итог
Constraint открывает члены в теле; inference сохраняет конкретику; default — запасной T, когда вывести не из чего.
TypeScript: ограничения и defaults у generics — как это работает? · Sobeso