В TypeScript всяка стойност може да бъде присвоена на типа „неизвестен“, но без твърдение за тип „неизвестен“ не може да бъде присвоен на нищо друго освен на себе си

Въведение

Не толкова отдавна, във версия 3.0, TypeScript въведе нов тип unknown към своя инструментариум, но какво прави този тип и по-конкретно кога трябва да го използваме?

Тази статия ще разгледа този нов тип, за да разбере по-добре неговата цел.

Ние също така последователно ще го сравняваме с неговия събрат, тип any; ако все още не сте запознати с него, вижте тази статия, където сме го обсъждали по-подробно.

Какъв е неизвестният тип?

В Typescript всяка стойност може да бъде присвоена на типа unknown, но без твърдение за тип, unknown не може да бъде присвоено на нищо друго освен на себе си и на типа any. По същия начин не се допускат операции върху стойност с тип, зададен като unknown, без първо да се потвърди или ограничи до по-точен тип.

Можем да присвоим произволна стойност на променлива от тип unknown по същия начин, както можем с променлива от тип any. За разлика от нашия случай, не можем да имаме достъп до свойства на променливи от тип unknown, нито да ги извикваме или конструираме.

Освен това стойности unknown могат да се присвояват само на променливи от типовете any и unknown.

Присвояване на стойности на променлива от тип unknown:

let val: unknown;

val = true; // Fine
val = 42; // Fine
val = "hey!"; // Fine
val = []; // Fine
val = {}; // Fine
val = Math.random; // Fine
val = null; // Fine
val = undefined; // Fine
val = () => { console.log('Hey again!'); }; // Fine

Присвояване на стойности от различни типове на променлива от тип any:

let val: any;

val = true; // Fine
val = 42; // Fine
val = "hey!"; // Fine
val = []; // Fine
val = {}; // Fine
val = Math.random; // Fine
val = null; // Fine
val = undefined; // Fine
val = () => { console.log('Hey again!'); }; // Fine

Присвояване на стойност от тип unknown на променливи от други типове:

let val: unknown;

const val1: unknown = val; // Fine
const val2: any = val; // Fine
const val3: boolean = val; // Will throw error
const val4: number = val; // Will throw error
const val5: string = val; // Will throw error
const val6: Record<string, any> = val; // Will throw error
const val7: any[] = val; // Will throw error
const val8: (...args: any[]) => void = val; // Will throw error

Правене на неизвестния тип по-специфичен

Можем да стесним възможните резултати от стойност от тип unknown.

Нека да разгледаме следния пример:

const isNumbersArray = (val: unknown): val is number[] => (
  Array.isArray(val) && val.every((element) => typeof element === 'number')
);
const unknownValue: unknown = [12, 2, 8, 17, 14];
if (isNumbersArray(unknownValue)) {
  const sum = unknownValue.reduce((accumulator, currentElement) => (accumulator + currentElement) , 0);
  console.log(sum);
}

Преди това типът беше unknown; обаче, след като извикахме isNumbersArray с unknownValue като негов аргумент, заключихме, че типът на unknownValue всъщност е този на масив от числа.

Кога ще трябва да използваме неизвестния тип?

Един конкретен сценарий, при който може да искаме да използваме типа unknown, е когато трябва да вземем нещо от локално хранилище.

Всички елементи на localStorage API се сериализират преди съхранение. Нашият случай на използване обаче включва стойността на извлечения елемент след десериализация.

Като използваме типа unknown, ще можем да напишем типа на десериализирания елемент, вместо просто да използваме анотацията any.

type ResultType =
  | { success: true; value: unknown }
  | { success: false; error: Error };
const retrieveItemFromLocalStorage = (key: string): ResultType => {
  const item = localStorage.getItem(key);
  if (!item) {
    // The item does not exist, thus return an error
    return {
      success: false,
      error: new Error(`Item with key "${key}" does not exist`),
    };
  }
  let value: unknown;
  try {
    value = JSON.parse(item);
  } catch (error) {
    // The item is not a valid JSON value, thus return an error
    return {
      success: false,
      error,
    };
  }
  // Everything's fine, thus return a successful result
  return {
    success: true,
    value,
  };
}

ResultType е „маркиран тип обединение“ (известен също като дискриминиран тип обединение). Може да срещнете Maybe, Option или Optional, които са неговия еквивалент на други езици.

Ние използваме ResultType, за да моделираме чисто успешните или неуспешните резултати от операцията.

И ето пример за това как бихме използвали нашата функция retrieveItemFromLocalStorage:

const result = retrieveItemFromLocalStorage("cached-blogs");

if (result.success) {
  // We've narrowed the `success` property to `true`,
  // so we can access the `value` property
  const cachedBlogs: unknown = result.value;

  if (isArrayOfBlogs(cachedBlogs)) {
    // We've narrowed the `unknown` type to `Array<Blog>`,
    // so we can safely use our cached `cachedBlogs` array as we would
    console.log('Cached blogs:', cachedBlogs);
  }
} else {
  // We've narrowed the `success` property to `false`,
  // so we can access the `error` property
  console.error(result.error);
}

Резюме

Надявам се, че сте се насладили на четенето и сте придобили малко повече представа за това какво представлява типът unknown, което го прави различен от типа any и кога обикновено го използваме в реални проекти.

Чувствайте се свободни да оставите вашите мисли в секцията за коментари по-долу.

наздраве!

Повече съдържание в PlainEnglish.io. Регистрирайте се за нашия безплатен седмичен бюлетин. Следвайте ни в Twitter, LinkedIn, YouTube и Discord . Интересувате ли се от Growth Hacking? Вижте Circuit.