Загрузка...
Загрузка...
Продолжая работу с платформой, вы принимаете условия Политики конфиденциальности и использование файлов cookie.
Type Assertions (Утверждения типов) — это способ сказать компилятору TypeScript: "Я знаю тип этого значения лучше, чем ты". Это не приведение типов и не изменяет значение во время выполнения — это только указание для компилятора.
TypeScript поддерживает два синтаксиса для type assertions:
const value: any = "hello";
const length = (value as string).length; // 5
const value: any = "hello";
const length = (<string>value).length; // 5
Важно:
В JSX/TSX можно использовать только синтаксис as, так как угловые скобки конфликтуют с синтаксисом JSX.
// TypeScript не знает, какой элемент вернется
const input = document.getElementById('email'); // HTMLElement | null
// Утверждаем конкретный тип
const emailInput = document.getElementById('email') as HTMLInputElement;
emailInput.value = 'test@example.com';
// Альтернатива
const emailInput2 = <HTMLInputElement>document.getElementById('email');
anyconst response: any = await fetch('/api/user').then(r => r.json());
interface User {
id: number;
name: string;
email: string;
}
const user = response as User;
console.log(user.name);
type Result = { success: true; data: string } | { success: false; error: string };
function handleResult(result: Result) {
if (result.success) {
// TypeScript знает, что это { success: true; data: string }
console.log(result.data);
} else {
// TypeScript знает, что это { success: false; error: string }
console.log(result.error);
}
}
// Но иногда нужно явно утвердить тип:
const result = getResult();
const successResult = result as { success: true; data: string };
// TypeScript выводит тип как string
const method = 'GET'; // string
// Нужен конкретный литерал
const method2 = 'GET' as const; // 'GET'
// Или
const method3 = 'GET' as 'GET' | 'POST';
const value: any = "42";
const num = value as number; // Компилируется, но ошибка в runtime!
console.log(num * 2); // NaN (строка умноженная на число)
// В других языках приведение типов меняет значение
String num = "42";
int converted = (int) num; // Реально преобразует строку в число
Важно:
Type Assertions не выполняют преобразование и не проверяют тип в runtime. Это только подсказка компилятору.
Иногда TypeScript не позволяет выполнить прямое утверждение между несовместимыми типами:
const value: string = "hello";
// Ошибка: Conversion of type 'string' to type 'number' may be a mistake
// const num = value as number;
// Двойное утверждение через unknown (или any)
const num = value as unknown as number;
Осторожно:
Двойные утверждения — признак проблемы в типизации. Используйте только в крайних случаях.
Оператор ! говорит TypeScript, что значение точно не null и не undefined.
// TypeScript думает, что может быть null
const element = document.getElementById('root'); // HTMLElement | null
// Мы уверены, что элемент существует
const elementNonNull = document.getElementById('root')!; // HTMLElement
elementNonNull.innerHTML = 'Hello';
interface User {
name: string;
email?: string;
}
const user: User = { name: 'John', email: 'john@example.com' };
// Ошибка: Object is possibly 'undefined'
// const emailLength = user.email.length;
// С проверкой
if (user.email) {
const emailLength = user.email.length;
}
// С non-null assertion (если уверены)
const emailLength = user.email!.length;
interface Config {
api?: {
url?: string;
};
}
const config: Config = {
api: { url: 'https://api.example.com' }
};
// Без non-null assertion
const url = config.api?.url; // string | undefined
// С non-null assertion
const urlNonNull = config.api!.url!; // string
Осторожно:
Non-null assertion обходит проверки TypeScript. Если значение окажется null или undefined, получите ошибку в runtime.
as const создает readonly литеральные типы.
// Обычное объявление
let x = 'hello'; // string
// С as const
let y = 'hello' as const; // 'hello' (литеральный тип)
// Обычный объект
const config = {
host: 'localhost',
port: 3000
};
// { host: string; port: number; }
// С as const
const configConst = {
host: 'localhost',
port: 3000
} as const;
// { readonly host: 'localhost'; readonly port: 3000; }
// Нельзя изменить
// configConst.port = 8080; // Ошибка
// Обычный массив
const colors = ['red', 'green', 'blue'];
// string[]
// С as const
const colorsConst = ['red', 'green', 'blue'] as const;
// readonly ['red', 'green', 'blue']
type Color = typeof colorsConst[number];
// 'red' | 'green' | 'blue'
// Создание union типа из массива
const ROLES = ['admin', 'user', 'guest'] as const;
type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest'
function checkRole(role: Role) {
// ...
}
checkRole('admin'); // OK
// checkRole('moderator'); // Ошибка
satisfies проверяет соответствие типу, но сохраняет вывод типов.
type Colors = 'red' | 'green' | 'blue';
const colors = {
red: [255, 0, 0],
green: '#00ff00',
blue: [0, 0, 255]
} satisfies Record<Colors, string | number[]>;
// TypeScript сохранил точные типы
colors.red; // number[]
colors.green; // string
colors.blue; // number[]
// Если бы использовали type assertion:
const colors2: Record<Colors, string | number[]> = {
red: [255, 0, 0],
green: '#00ff00',
blue: [0, 0, 255]
};
colors2.red; // string | number[] (потеряли точность)
// Плохо
function getUser() {
return { id: 1, name: 'John' } as any;
}
// Хорошо
interface User {
id: number;
name: string;
}
function getUser(): User {
return { id: 1, name: 'John' };
}
// Плохо - скрываем проблему
const value: number = "hello" as any as number;
// Хорошо - исправляем проблему
const value: string = "hello";
const num: number = parseInt(value, 10);
function processValue(value: string | number) {
// Плохо
const str = value as string;
return str.toUpperCase();
// Хорошо
if (typeof value === 'string') {
return value.toUpperCase();
}
return value.toString();
}
function isString(value: any): value is string {
return typeof value === 'string';
}
const value: any = "hello";
if (isString(value)) {
// TypeScript знает, что value - это string
console.log(value.toUpperCase());
}
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; size: number };
function getArea(shape: Shape) {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.size ** 2;
}
}
// Вместо non-null assertion
const value = obj.prop!.nested!.value;
// Используйте optional chaining
const value = obj.prop?.nested?.value; // string | undefined
const num = 42;
// Ошибка: невозможно преобразовать number в string
// const str = num as string;
// Нужно двойное утверждение (но это плохо!)
const str = num as unknown as string;
as any// Плохо
function processData(data: ComplexType) {
return (data as any).someMethod();
}
// Хорошо
function processData(data: ComplexType) {
if ('someMethod' in data && typeof data.someMethod === 'function') {
return data.someMethod();
}
}
// Плохо - теряем типобезопасность
const users = getUsers() as any[];
// Хорошо
interface User {
id: number;
name: string;
}
const users = getUsers() as User[];
function getElement<T extends HTMLElement>(id: string): T | null {
return document.getElementById(id) as T | null;
}
const input = getElement<HTMLInputElement>('email');
if (input) {
input.value = 'test';
}
function parseJSON(json: string): unknown {
return JSON.parse(json);
}
const data = parseJSON('{"name": "John"}');
// Type guard для безопасности
function isUser(data: unknown): data is { name: string } {
return typeof data === 'object' &&
data !== null &&
'name' in data &&
typeof (data as any).name === 'string';
}
if (isUser(data)) {
console.log(data.name);
}
Type Assertions:
as (предпочтительно) или <> (не в JSX)any! убирает null | undefinedas const создает readonly литералыsatisfies (4.9+) проверяет тип, сохраняя выводНа собеседовании:
Важно уметь:
as и <>)as const