TypeScript може визначити (а потім перевірити) тип змінної на основі кількох простих правил. Тому що ці правила прості, ви можете навчити свій мозок розпізнавати безпечний/небезпечний код (це сталося зі мною та моїми товаришами по команді досить швидко).
Потік типів – це саме те, як я уявляю в своєму мозку потік інформації про типи.
Типи змінної виводяться за визначенням.
let foo = 123; // foo is a `number`
let bar = "Hello"; // bar is a `string`
foo = bar; // Error: cannot assign `string` to a `number`
Це приклад типів, що перетікають справа наліво.
Тип повернення визначається операторами return, наприклад. передбачається, що наступна функція повертає number
.
function add(a: number, b: number) {
return a + b;
}
Це приклад типів, що випливають знизу.
Тип параметрів функції/повернених значень також можна визначити за допомогою призначення, наприклад. тут ми говоримо, що foo
є Adder
, що робить number
типом a
і b
.
type Adder = (a: number, b: number) => number;
let foo: Adder = (a, b) => a + b;
Цей факт можна продемонструвати наведеним нижче кодом, який викликає помилку, як ви сподіваєтесь:
type Adder = (a: number, b: number) => number;
let foo: Adder = (a, b) => {
a = "hello"; // Error: cannot assign `string` to a `number`
return a + b;
}
Це приклад типів, що перетікають зліва направо.
Той самий спосіб визначення типу призначення працює, якщо ви створюєте функцію для аргументу зворотного виклику. Зрештою, argument -> parameter
- це просто інша форма призначення змінних.
type Adder = (a: number, b: number) => number;
function iTakeAnAdder(adder: Adder) {
return adder(1, 2);
}
iTakeAnAdder((a, b) => {
// a = "hello"; // Would Error: cannot assign `string` to a `number`
return a + b;
})
Ці прості правила також працюють за наявності structuring (створення буквального об’єкта). Наприклад, у наступному випадку тип foo
вважається {a:number, b:number}
let foo = {
a: 123,
b: 456
};
// foo.a = "hello"; // Would Error: cannot assign `string` to a `number`
Аналогічно для масивів:
const bar = [1,2,3];
// bar[0] = "hello"; // Would error: cannot assign `string` to a `number`
І звичайно будь-яке вкладення:
let foo = {
bar: [1, 3, 4]
};
// foo.bar[0] = 'hello'; // Would error: cannot assign `string` to a `number`
І, звичайно, вони також працюють з деструктуризацією обох об’єктів:
let foo = {
a: 123,
b: 456
};
let {a} = foo;
// a = "hello"; // Would Error: cannot assign `string` to a `number`
and arrays:
const bar = [1, 2];
let [a, b] = bar;
// a = "hello"; // Would Error: cannot assign `string` to a `number`
І якщо параметр функції можна вивести, то можна вивести і його деструктуровані властивості. Наприклад, тут ми деструктуруємо аргумент на члени a
/b
.
type Adder = (numbers: { a: number, b: number }) => number;
function iTakeAnAdder(adder: Adder) {
return adder({ a: 1, b: 2 });
}
iTakeAnAdder(({a, b}) => { // Types of `a` and `b` are inferred
// a = "hello"; // Would Error: cannot assign `string` to a `number`
return a + b;
})
Ми вже бачили, як Type Guards допомагає змінювати та звужувати типи (зокрема, у випадку об’єднань). Захисники типу — це просто інша форма виведення типу для змінної в блоці.
Типи не входять у параметри функції, якщо це не можна вивести з призначення. Наприклад, у наступному випадку компілятор не знає типу foo
, тому він не може визначити тип a
або b
.
const foo = (a,b) => { /* do something */ };
Однак, якщо було введено foo
, можна визначити тип параметрів функції (обидва a
, b
вважаються типом number
у прикладі нижче).
type TwoNumberFunction = (a: number, b: number) => void;
const foo: TwoNumberFunction = (a, b) => { /* do something */ };
Хоча TypeScript загалом може визначити тип повернення функції, це може бути не те, що ви очікуєте. Наприклад, тут функція foo
має тип повернення any
.
function foo(a: number, b: number) {
return a + addOne(b);
}
// Some external function in a library someone wrote in JavaScript
function addOne(c) {
return c + 1;
}
Це тому, що на тип повернення впливає погане визначення типу для addOne
(c
- це any
, тому повернення addOne
є any
, тому повернення foo
є any
).
Я вважаю найпростішим завжди чітко говорити про повернення функції. Зрештою, ці анотації є теоремою, а тіло функції є доказом.
Є й інші випадки, які можна уявити, але хороша новина полягає в тому, що існує прапор компілятора, який може допомогти виявити такі помилки.
Прапор noImplicitAny
в наказує компілятору викликати помилку, якщо він не може визначити тип змінної (і тому може мати її лише як implicit any
тип). Тоді можна
- або скажіть, що yes I want it to be of type
any
додавши explicitly анотацію типу: any
- допоможіть компілятору, додавши ще кілька вірних анотацій.