Тип не проходить обмеження
Якщо generic-параметр не задовольняє обмеженню (extends), TypeScript видасть помилку на етапі компіляції - тобто код не скомпілюється, поки ти не підставиш відповідний тип.
Що означає "не задовольняє обмеженню"
Коли ти пишеш, наприклад:
function fn<T extends { id: number }>(value: T) { ... }ти кажеш TypeScript:
"Дозволяй лише такі типи
T, у яких обов'язково є полеid: number."
Якщо ти спробуєш передати тип без id, компілятор скаже: "Type X does not satisfy the constraint Y".
Приклад 1: Помилка при невідповідності constraint
function printId<T extends { id: number }>(obj: T) {
console.log(obj.id);
}
printId({ id: 42 }); // підходить
printId({ name: "Tim" }); // Помилка:
// Argument of type '{ name: string }' is not assignable to parameter of type '{ id: number; }'.
Tповинен бути підтипом{ id: number }, але{ name: string }не підходить.
Приклад 2: Невідповідність об'єднанню
function toStringValue<T extends string | number>(value: T): string {
return value.toString();
}
toStringValue("hello"); // ок
toStringValue(100); // ок
toStringValue(true); // Error: 'boolean' does not satisfy the constraint 'string | number'.Тип
booleanне входить у дозволену множину (string | number).
Приклад 3: Невідповідність при залежному параметрі
function getProp<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const user = { id: 1, name: "Tim" };
getProp(user, "id"); // ок
getProp(user, "age"); // Error: Type '"age"' is not assignable to parameter of type '"id" | "name"'
Kповинен бути ключем об'єктаT, але"age"таким не є.
Приклад 4: Невідповідність обмеженню object
function logKeys<T extends object>(obj: T) {
console.log(Object.keys(obj));
}
logKeys({ a: 1 }); // ок
logKeys(42); // Error: number does not satisfy the constraint 'object'Примітиви (
number,string,boolean) не є об'єктами.
Приклад 5: Невідповідність для класів (constructor constraints)
type Constructor<T> = new (...args: any[]) => T;
function createInstance<T extends Constructor<any>>(Ctor: T) {
return new Ctor();
}
class Person {}
createInstance(Person); // ок
createInstance(123); // Error: number does not satisfy the constraint 'new (...args: any[]) => any'
123не є конструктором, тому не підходить під обмеження.
Приклад 6: Помилка при порушенні шаблонного обмеження
type Prefixed<T extends `id_${string}`> = { key: T };
const good: Prefixed<"id_123"> = { key: "id_123" }; // ок
const bad: Prefixed<"user_1"> = { key: "user_1" }; // Type '"user_1"' does not satisfy the constraint '`id_${string}`'
Tповинен відповідати шаблонному рядкуid_....
Що відбувається під капотом
Коли TypeScript бачить T extends U, він перевіряє:
Чи може тип
Tбути присвоєним типуU(isTassignable toU).
Якщо ні - помилка компіляції:
Type 'T' does not satisfy the constraint 'U'.TypeScript не намагається автоматично перетворити типи - він просто забороняє невідповідність.
Приклад з явним вказанням типу
function identity<T extends number>(value: T) {
return value;
}
identity(42); // ок
identity<number>(42); // ок
identity<string>("hi"); // Type 'string' does not satisfy the constraint 'number'Навіть якщо ти вкажеш generic явно, компілятор перевірить, чи підходить він під
extends.
Чому це корисно
- Типобезпека: ти не зможеш випадково передати непідходящий тип.
- Автодоповнення: усередині функції TS знає, які властивості гарантовані.
- Гнучкість: можна задати "м'які" обмеження (наприклад,
{ id: any }).
Підсумок
| Ситуація | Приклад | Що станеться |
|---|---|---|
| Тип задовольняє обмеженню | <T extends { id: number }> + { id: 1 } | Працює |
| Тип не має потрібних властивостей | { name: string } | Помилка |
| Тип не входить в об'єднання | `<T extends string | number>+boolean` |
| Ключ не існує | <K extends keyof T> + "age" | Помилка |
| Тип не об'єкт | <T extends object> + number | Помилка |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.