← Назад к вопросам

Для чего использовать unknown?

2.0 Middle🔥 162 комментариев
#Soft Skills и рабочие процессы

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Для чего использовать unknown в TypeScript

unknown - это тип для значения, типа которого мы не знаем на момент разработки. Это более безопасная альтернатива any. В строгом TypeScript нужно использовать unknown вместо any.

unknown vs any

any - отключает типобезопасность полностью:

const value: any = 42;

// TypeScript не ругается, хотя value это число
value.toUpperCase(); // Ошибка будет только в runtime!
value.map(x => x * 2); // Тоже скомпилируется, но упадёт в runtime

unknown - заставляет проверить тип перед использованием:

const value: unknown = 42;

// TypeScript ошибка: Object is of type 'unknown'
value.toUpperCase();
value.map(x => x * 2);

// Нужно сначала проверить тип
if (typeof value === 'string') {
  value.toUpperCase(); // OK
}

Когда использовать unknown

1. API ответы (JSON парсинг)

// Плохо: используем any
const response: any = await fetch('/api/data').then(r => r.json());
response.user.email; // Может быть undefined

// Хорошо: используем unknown и проверяем
const response: unknown = await fetch('/api/data').then(r => r.json());

if (isValidResponse(response)) {
  response.user.email; // TypeScript знает структуру
}

function isValidResponse(obj: unknown): obj is ApiResponse {
  return (
    typeof obj === 'object' &&
    obj !== null &&
    'user' in obj &&
    typeof (obj as any).user === 'object'
  );
}

2. Обработка ошибок

// Плохо: catch получает any
try {
  doSomething();
} catch (error: any) {
  console.log(error.message); // Может упасть если error не Error
}

// Хорошо: используем unknown
try {
  doSomething();
} catch (error: unknown) {
  if (error instanceof Error) {
    console.log(error.message);
  } else {
    console.log('Unknown error:', error);
  }
}

3. localStorage / sessionStorage

// Плохо: any
const data: any = JSON.parse(localStorage.getItem('user') || 'null');
data.name; // Может быть null или не иметь свойства name

// Хорошо: unknown с проверкой
const data: unknown = JSON.parse(localStorage.getItem('user') || 'null');

interface User {
  id: string;
  name: string;
  email: string;
}

function isUser(obj: unknown): obj is User {
  return (
    typeof obj === 'object' &&
    obj !== null &&
    'id' in obj && typeof (obj as any).id === 'string' &&
    'name' in obj && typeof (obj as any).name === 'string' &&
    'email' in obj && typeof (obj as any).email === 'string'
  );
}

if (isUser(data)) {
  console.log(data.name); // Безопасно
}

Type Guards с unknown

Функция type guard:

// Проверить, является ли значение string
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

// Проверить, является ли значение массивом определённого типа
function isStringArray(value: unknown): value is string[] {
  return Array.isArray(value) && value.every(item => typeof item === 'string');
}

// Проверить, является ли значение объектом с определённой структурой
interface Post {
  id: number;
  title: string;
  content: string;
}

function isPost(value: unknown): value is Post {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value && typeof (value as any).id === 'number' &&
    'title' in value && typeof (value as any).title === 'string' &&
    'content' in value && typeof (value as any).content === 'string'
  );
}

// Использование
const unknownValue: unknown = fetchData();

if (isString(unknownValue)) {
  console.log(unknownValue.toUpperCase());
} else if (isStringArray(unknownValue)) {
  unknownValue.forEach(item => console.log(item));
} else if (isPost(unknownValue)) {
  console.log(unknownValue.title);
}

На практике: обработка данных с сервера

interface ApiUser {
  id: string;
  name: string;
  email: string;
  age?: number;
}

function isApiUser(obj: unknown): obj is ApiUser {
  if (typeof obj !== 'object' || obj === null) return false;
  
  const typed = obj as Record<string, unknown>;
  
  return (
    typeof typed.id === 'string' &&
    typeof typed.name === 'string' &&
    typeof typed.email === 'string' &&
    (typed.age === undefined || typeof typed.age === 'number')
  );
}

async function fetchUser(id: string): Promise<ApiUser> {
  const response = await fetch(`/api/users/${id}`);
  const data: unknown = await response.json();
  
  if (!isApiUser(data)) {
    throw new Error('Invalid user data from API');
  }
  
  return data; // Теперь TypeScript знает, что это ApiUser
}

// Использование
try {
  const user = await fetchUser('123');
  console.log(user.name); // Безопасно
} catch (error: unknown) {
  if (error instanceof Error) {
    console.error(error.message);
  }
}

Использование unknown в функциях

// Параметр unknown - функция принимает что угодно
function processValue(value: unknown): string {
  if (typeof value === 'string') return value.toUpperCase();
  if (typeof value === 'number') return `Number: ${value}`;
  if (Array.isArray(value)) return `Array with ${value.length} items`;
  return 'Unknown type';
}

processValue('hello'); // "HELLO"
processValue(42); // "Number: 42"
processValue([1, 2, 3]); // "Array with 3 items"

// Возвращаемый unknown - результат может быть любым
function getValue(key: string): unknown {
  return localStorage.getItem(key);
}

const result = getValue('some-key');
if (typeof result === 'string') {
  // Используем как string
}

Библиотеки для валидации

Вместо ручного type guard можно использовать валидационные библиотеки:

// Zod
import { z } from 'zod';

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
  age: z.number().optional()
});

type User = z.infer<typeof UserSchema>;

const data: unknown = await fetchData();
const user = UserSchema.parse(data); // Выбросит ошибку если невалидно

// Yup
import * as yup from 'yup';

const UserSchema = yup.object().shape({
  id: yup.string().required(),
  name: yup.string().required(),
  email: yup.string().email().required()
});

const data: unknown = await fetchData();
const user = await UserSchema.validate(data);

Сравнение подходов

// 1. any - опасно, теряем типобезопасность
const value: any = fetchData();
value.nonExistent.property(); // Компилируется, но упадёт в runtime

// 2. unknown - безопасно, требует проверки
const value: unknown = fetchData();
value.nonExistent.property(); // Ошибка компиляции!

if (typeof value === 'object' && value !== null && 'nonExistent' in value) {
  // Теперь можно использовать
}

// 3. Специфичный тип - лучше всего, если известна структура
interface Data {
  nonExistent?: { property: string };
}

const value: Data = fetchData();
value.nonExistent?.property; // Безопасно и без лишних проверок

Вывод

Используй unknown когда:

  • Не знаешь тип данных (JSON с сервера, localStorage)
  • Обрабатываешь ошибки в catch блоке
  • Пишешь generic функции, которые должны работать с разными типами
  • Получаешь данные из внешних источников

Преимущества unknown:

  • Безопаснее any
  • Требует явной проверки типа
  • Ловит ошибки на этапе разработки
  • Самодокументируемый код (видно, что тип неизвестен)

Правило: Никогда не используй any без очень серьёзной причины. Всегда выбирай unknown.

Для чего использовать unknown? | PrepBro