У TypeScript є два окремих «простори імен»:
- простір значень - те, що існує під час виконання: змінні, функції, класи, об'єкти;
- простір типів - те, що існує лише для компілятора і зникає в JavaScript:
type,interface.
Одне ім'я може одночасно бути і значенням, і типом - і це різні сутності:
const Status = { Draft: 'draft', Published: 'published' } as const; // значення
type Status = (typeof Status)[keyof typeof Status]; // тип з тим самим іменем
function setStatus(status: Status) {} // тут Status - тип
setStatus(Status.Draft); // тут Status - значення
Що належить до обох просторів одразу:
- клас - і конструктор (значення), і тип екземпляра:
class User { constructor(public name: string) {} }
const u: User = new User('Оля'); // User - тип екземпляра і значення-конструктор
type UserCtor = typeof User; // тип самого класу (конструктора)
enum- об'єкт під час виконання і тип його значень;- простори імен (
namespace) з кодом.
Переходи між просторами:
typeof значення- отримати тип зі значення:typeof config,typeof fetchUser;ReturnType<typeof fn>,InstanceType<typeof User>- похідні типи;- зворотного переходу немає: тип не може стати значенням. Неможливо пройтися циклом по ключах
interfaceпід час виконання, перевіритиinstanceofдляtypeчи отримати список значень з об'єднання літералів.
Практичні наслідки:
- джерело правди - значення, тип з нього виводять. Масив
['draft', 'published'] as constдає і список для<select>, і тип(typeof STATUSES)[number]. Навпаки - з типу список не отримати; - схеми валідації (Zod) - те саме: схема - значення, що існує під час виконання, а тип виводиться з неї (
z.infer<typeof schema>); import typeімпортує лише з простору типів - такий імпорт гарантовано зникає після компіляції. ЗverbatimModuleSyntaxкомпілятор вимагає позначати імпорти лише типів явно;- помилка «X only refers to a type, but is being used as a value here» - спроба використати тип як значення (
instanceof Interface,Object.keys(SomeType)).