Перейти к содержимому

Type Inference Pitfalls

Topic для разговора с senior’ом: где TS «угадывает» неудачно. На интервью спросят про widening литералов, контекстуальную типизацию callback’ов, проблему const arr: string[] = [‘a’,‘b’] теряет литералы. Красный флаг — лепить везде as const, когда хватает satisfies.

Widening литералов и решение через as const / satisfies:

const o1 = { dir: 'asc' }; // { dir: string } — широко
const o2 = { dir: 'asc' } as const; // { readonly dir: 'asc' }
const o3 = { dir: 'asc' } satisfies { dir: 'asc' | 'desc' };
// o3.dir — 'asc', при этом проверено, что значение валидно для union

Contextual typing в массивах и колбэках:

// Без явного типа массива literal'ы расширяются до string
const tags1 = ['a', 'b']; // string[]
const tags2 = ['a', 'b'] as const; // readonly ['a', 'b']
// Внутри map TS типизирует параметр по контексту
[1, 2, 3].map((n /* number */) => n * 2);
// А вот вытащенный callback теряет контекст
const double = (n) => n * 2; // n: any (без noImplicitAny — ошибка)

Generic-инференс: TS не «домысливает» из возврата, и NoInfer (TS 5.4+) помогает закрепить ожидание:

function pickDefault<T>(items: T[], fallback: NoInfer<T>): T {
return items[0] ?? fallback;
}
pickDefault([1, 2, 3], 0); // T = number
// pickDefault<number>(['a','b'], 0); // ошибка: 'a' не number
// без NoInfer: TS вывёл бы T = number | string из обоих аргументов

Итог: Инференс расширяет литералы, теряет контекст вне обёртывающего вызова и «усредняет» T по всем источникам — satisfies, as const и NoInfer правят это явно.