Ошибка TypeScript: Type 'string' is not assignable to type — как исправить?
Почему возникает ошибка и как её правильно исправить
Ошибка Type 'string' is not assignable to type возникает, когда TypeScript видит несоответствие между ожидаемым типом (указанным в интерфейсе) и фактическим типом значения, которое вы пытаетесь присвоить. В вашем случае:
- age — ожидается
number, а приходитstring - role — ожидается union-тип
'admin' | 'user', а приходитstring
Использование as (type assertion) действительно не рекомендуется в таких ситуациях, потому что это подавляет проверку типов и может привести к ошибкам в рантайме. Вместо этого нужно явно преобразовывать данные.
Правильное решение: валидация и преобразование
Лучший подход — создать функцию-валидатор, которая принимает "сырые" данные, проверяет их и возвращает объект с корректными типами. Вот пример:
interface User {
name: string;
age: number;
role: 'admin' | 'user';
}
function createUser(rawData: { name: string; age: string; role: string }): User {
const age = Number(rawData.age);
if (isNaN(age) || age < 0) {
throw new Error('Некорректный возраст');
}
if (rawData.role !== 'admin' && rawData.role !== 'user') {
throw new Error('Роль должна быть admin или user');
}
return {
name: rawData.name,
age,
role: rawData.role as 'admin' | 'user' // здесь as безопасен, так как мы уже проверили
};
}
// Использование
const formData = { name: 'Иван', age: '25', role: 'admin' };
const user = createUser(formData); // ✅ Работает
Альтернативный подход: type guard
Можно использовать пользовательский type guard для проверки role:
function isUserRole(value: string): value is 'admin' | 'user' {
return value === 'admin' || value === 'user';
}
function parseUser(data: any): User {
if (typeof data.name !== 'string') throw new Error('name должен быть строкой');
if (typeof data.age !== 'string') throw new Error('age должен быть строкой');
if (!isUserRole(data.role)) throw new Error('Некорректная роль');
const age = Number(data.age);
if (isNaN(age)) throw new Error('age не является числом');
return {
name: data.name,
age,
role: data.role
};
}
Что делать, если данных много?
Для больших проектов рекомендую использовать библиотеки валидации, например Zod:
import { z } from 'zod';
const UserSchema = z.object({
name: z.string(),
age: z.coerce.number().positive(),
role: z.enum(['admin', 'user'])
});
const rawData = { name: 'Иван', age: '25', role: 'admin' };
const user = UserSchema.parse(rawData); // ✅ age автоматически преобразуется в число
// user автоматически получает тип User
Важно: Никогда не используйте as для "затыкания" ошибок типов без проверки данных. TypeScript — это инструмент для безопасности, а не просто синтаксический сахар.
Простое решение для небольших проектов
Если вы не хотите писать сложные валидаторы, можно использовать промежуточный интерфейс для "сырых" данных и явное приведение типов с проверкой:
interface RawUser {
name: string;
age: string;
role: string;
}
interface User {
name: string;
age: number;
role: 'admin' | 'user';
}
function convertToUser(raw: RawUser): User | null {
const age = parseInt(raw.age, 10);
if (isNaN(age)) return null;
const validRoles = ['admin', 'user'] as const;
if (!validRoles.includes(raw.role as any)) return null;
return {
name: raw.name,
age,
role: raw.role as 'admin' | 'user'
};
}
Дополнительный совет: работа с формами
Для данных из HTML-форм можно использовать HTMLFormElement и FormData. Вот пример с обработкой submit:
document.getElementById('myForm')?.addEventListener('submit', (e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const formData = new FormData(form);
const rawUser = {
name: formData.get('name') as string,
age: formData.get('age') as string,
role: formData.get('role') as string
};
const user = convertToUser(rawUser);
if (user) {
console.log('Пользователь создан:', user);
} else {
console.error('Некорректные данные формы');
}
});
Этот подход не такой надёжный, как полноценная валидация, но для простых скриптов или прототипов вполне подходит. Главное — всегда проверять данные перед приведением типов.
Ответов пока нет.
Войдите чтобы оставить ответ