предисловие
«Мама, я хочу писать на TypeScript», «Так много ошибок, хватит, малыш?»
"use strict"инструкцииJavaScript 1.8.5 (ECMAScript5)Добавить новое.
До сих пор разработчики внешнего интерфейса в основном включали строгий режим для ввода кода по умолчанию.
Ну ты знаешьTypescriptЕсть ли у него на самом деле свой строгий режим?
1. Typescriptправила строгого режима
когдаTypescriptСтрогий режим установлен наon, он будет использовать strictСтрогие правила типизации в семействе выполняют проверку кода для всех файлов в проекте. Правила таковы:
| имя правила | объяснять |
|---|---|
noImplicitAny |
Переменные или параметры функции не могут иметь неявные значения.anyТипы. |
noImplicitThis |
не положеноthisКонтекст определяется неявно. |
strictNullChecks |
не положеноnullилиundefinedвозможность. |
strictPropertyInitialization |
Проверяет определенные свойства до и после инициализации внутри конструктора. |
strictBindCallApply |
правильноbind, call, applyБолее строгая проверка типов. |
strictFunctionTypes |
Строго контравариантное сравнение аргументов функций. |
2. noImplicitAny
Это правило не позволяет переменным или параметрам функций иметь неявные значения.anyТипы. См. следующий пример:
// Javascript/Typescript 非严格模式
function extractIds (list) {
return list.map(member => member.id)
}
Приведенный выше пример неверенlistограничение типа,mapзацикленныйitemпараметрmember.
пока вTypescriptВ строгом режиме произойдет следующая ошибка:
// Typescript 严格模式
function extractIds (list) {
// ❌ ^^^^
// Parameter 'list' implicitly
// has an 'any' type. ts(7006)
return list.map(member => member.id)
// ❌ ^^^^^^
// Parameter 'member' implicitly
// has an 'any' type. ts(7006)
}
Правильное написание должно быть:
// Typescript 严格模式
interface Member {
id: number
name: string
}
function extractIds (list: Member[]) {
return list.map(member => member.id)
}
1.1 Как обрабатывать собственные события браузера?
Браузер поставляется с событиями, такими какe.preventDefault(), это ключевой код, который предотвращает поведение браузера по умолчанию.
это вTypescriptВ строгом режиме будет сообщено об ошибке:
// Typescript 严格模式
function onChangeCheckbox (e) {
// ❌ ^
// Parameter 'e' implicitly
// has an 'any' type. ts(7006)
e.preventDefault()
const value = e.target.checked
validateCheckbox(value)
}
Если вам нужно использовать его в обычном режиме
Web API, вам необходимо определить расширение глобально. Например:
// Typescript 严格模式
interface ChangeCheckboxEvent extends MouseEvent {
target: HTMLInputElement
}
function onChangeCheckbox (e: ChangeCheckboxEvent) {
e.preventDefault()
const value = e.target.checked
validateCheckbox(value)
}
1.2 Сторонние библиотеки также должны определять типы
Обратите внимание, что если вы импортируете не-Typescriptбиблиотеку, это также вызывает ошибку, потому что тип импортируемой библиотекиany.
// Typescript 严格模式
import { Vector } from 'sylvester'
// ❌ ^^^^^^^^^^^
// Could not find a declaration file
// for module 'sylvester'.
// 'sylvester' implicitly has an 'any' type.
// Try `npm install @types/sylvester`
// if it exists or add a new declaration (.d.ts)
// file containing `declare module 'sylvester';`
// ts(7016)
Это может быть рефакторинг проектаTypescriptБольшая беда для версии, нужно конкретно определить тип интерфейса сторонней библиотеки
3. noImplicitThis
Это правило не позволяетthisКонтекст определяется неявно. См. следующий пример:
// Javascript/Typescript 非严格模式
function uppercaseLabel () {
return this.label.toUpperCase()
}
const config = {
label: 'foo-config',
uppercaseLabel
}
config.uppercaseLabel()
// FOO-CONFIG
В нестрогом режимеthisнаправлениеconfigобъект.this.labelпросто ищиconfig.label.
но,thisСсылки на функции могут быть неоднозначными:
// Typescript严格模式
function uppercaseLabel () {
return this.label.toUpperCase()
// ❌ ^^^^
// 'this' implicitly has type 'any'
// because it does not have a type annotation. ts(2683)
}
Если выполнять в одиночкуthis.label.toUpperCase(), потому чтоthisконтекстconfigбольше не существует и сообщить об ошибке, потому чтоlabelнеопределенный.
Один из способов решения этой проблемы — избегатьthisИспользовать функцию без контекста:
// Typescript严格模式
const config = {
label: 'foo-config',
uppercaseLabel () {
return this.label.toUpperCase()
}
}
Лучшим подходом было бы написать интерфейс, определяющий все типы, вместоTypescriptсделать вывод:
// Typescript严格模式
interface MyConfig {
label: string
uppercaseLabel: (params: void) => string
}
const config: MyConfig = {
label: 'foo-config',
uppercaseLabel () {
return this.label.toUpperCase()
}
}
4. strictNullChecks
Это правило не позволяетnullилиundefinedвозможность. См. следующий пример:
// Typescript 非严格模式
function getArticleById (articles: Article[], id: string) {
const article = articles.find(article => article.id === id)
return article.meta
}
TypescriptВ нестрогом режиме нет проблем с записью таким образом. Но строгий режим доставит вам немного хлопот:
«Ты не можешь этого сделать, еслиfindНе совпало значение? ":
// Typescript严格模式
function getArticleById (articles: Article[], id: string) {
const article = articles.find(article => article.id === id)
return article.meta
// ❌ ^^^^^^^
// Object is possibly 'undefined'. ts(2532)
}
«Я звезда, ты звезда!»
Таким образом, вы бы изменили его, чтобы он выглядел так:
// Typescript严格模式
function getArticleById (articles: Article[], id: string) {
const article = articles.find(article => article.id === id)
if (typeof article === 'undefined') {
throw new Error(`Could not find an article with id: ${id}.`)
}
return article.meta
}
"Это вкусно!"
5. strictPropertyInitialization
Это правило будет проверять свойства, которые были определены до и после инициализации внутри конструктора.
Вы должны убедиться, что свойства каждого экземпляра имеют начальные значения, которые могут быть назначены в конструкторе или при определении свойства.
(strictPropertyInitialization, это вонючее имя очень похожеReactМного произвольных атрибутов в исходном коде)
См. следующий пример:
// Typescript非严格模式
class User {
username: string;
}
const user = new User();
const username = user.username.toLowerCase();
Если включен строгий режим, проверка типов сообщит о дальнейших ошибках:
class User {
username: string;
// ❌ ^^^^^^
// Property 'username' has no initializer
// and is not definitely assigned in the constructor
}
const user = new User();
/
const username = user.username.toLowerCase();
// ❌ ^^^^^^^^^^^^
// TypeError: Cannot read property 'toLowerCase' of undefined
Есть четыре решения.
Сценарий №1: Разрешитьundefined
дляusernameопределения свойств обеспечиваютundefinedТипы:
class User {
username: string | undefined;
}
const user = new User();
usernameсвойства могут бытьstring | undefinedтипа, но пишите так,Необходимо убедиться, что значениеstringТипы:
const username = typeof user.username === "string"
? user.username.toLowerCase()
: "n/a";
это не слишкомTypescript.
Сценарий № 2: Явная инициализация значений свойств
Этот метод немного неуклюж, но он работает:
class User {
username = "n/a";
}
const user = new User();
// OK
const username = user.username.toLowerCase();
Сценарий №3: присвоить значение в конструкторе
Наиболее полезным решением являетсяusernameКонструктор добавляет параметры, затем назначьте егоusernameАтрибуты.
Таким образом, всякий раз, когдаnew User(), оба должны предоставлять значения по умолчанию в качестве параметров:
class User {
username: string;
constructor(username: string) {
this.username = username;
}
}
const user = new User("mariusschulz");
// OK
const username = user.username.toLowerCase();
Может такжеpublicМодификатор еще больше упрощает:
class User {
constructor(public username: string) {}
}
const user = new User("mariusschulz");
// OK
const username = user.username.toLowerCase();
Сценарий № 4: явное утверждение присваивания
В некоторых случаях свойства инициализируются косвенно (с помощью вспомогательных методов или библиотек внедрения зависимостей).
В этом случае вы можете использовать свойствоявное утверждение присваиваниячтобы помочь системе типов идентифицировать типы.
class User {
username!: string;
constructor(username: string) {
this.initialize(username);
}
private initialize(username: string) {
this.username = username;
}
}
const user = new User("mariusschulz");
// OK
const username = user.username.toLowerCase();
сквозьusernameдобавляет явное утверждение присваивания, мы сообщаем средству проверки типов:username, он может ожидать, что свойство будет инициализировано, даже если не может обнаружить само свойство.
6. strictBindCallApply
Это правило будетbind, call, applyОпределяйте типы более строго.
Что ты имеешь в виду? См. следующий пример:
// JavaScript
function sum (num1: number, num2: number) {
return num1 + num2
}
sum.apply(null, [1, 2])
// 3
Когда вы не помните тип параметра, тип и номер параметра не проверяются в нестрогом режиме.При запуске кодаTypescriptи среда (вероятно, браузер) не выдает ошибку:
// Typescript非严格模式
function sum (num1: number, num2: number) {
return num1 + num2
}
sum.apply(null, [1, 2, 3])
// 还是...3?
а такжеTypescriptВ строгом режиме это запрещено:
// Typescript严格模式
function sum (num1: number, num2: number) {
return num1 + num2
}
sum.apply(null, [1, 2, 3])
// ❌ ^^^^^^^^^
// Argument of type '[number, number, number]' is not
// assignable to parameter of type '[number, number]'.
// Types of property 'length' are incompatible.
// Type '3' is not assignable to type '2'. ts(2345)
Что делать тогда?“...”оператор спреда иreduceстарый друг пришел на помощь:
// Typescript严格模式
function sum (...args: number[]) {
return args.reduce<number>((total, num) => total + num, 0)
}
sum.apply(null, [1, 2, 3])
// 6
7. strictFunctionTypes
Это правило будет проверять и ограничивать параметры типа функции, чтобы они были инвариантными (contravariantly) вместо двойного (bivariantly, то есть ковариантный или устойчивый).
На первый взгляд внутренняя ОС: «Что это?», вот введение:
Ковариантные и контравариантные вики очень сложны, но принцип на самом деле один.
- Подтипы могут быть неявно преобразованы в супертипы.
Чтобы привести пример, который легче всего понять,intа такжеfloatДва типа отношений могут быть записаны следующим образом.int≦float: то естьintдаfloatподтип .
Эта более строгая проверка применяется ко всем типам функций, кроме объявлений методов или конструкторов. Методы специально исключены, чтобы гарантировать, что универсальные классы и интерфейсы (такие как Array ) в целом остаются ковариантными.
Посмотрите на следующееAnimalдаDogа такжеCatПример супертипа:
declare let f1: (x: Animal) => void;
declare let f2: (x: Dog) => void;
declare let f3: (x: Cat) => void;
f1 = f2; // 启用 --strictFunctionTypes 时错误
f2 = f1; // 正确
f2 = f3; // 错误
- Первый оператор присваивания разрешен в режиме проверки типов по умолчанию, но помечен как ошибка в режиме строгого ввода функций.
- И шаблон строгой функциональной типизации помечает это как ошибку, потому что это не может быть оправдано.
- В любом шаблоне третье присваивание неверно, потому что оно никогда не имеет смысла.
Другой способ описать этот пример состоит в том, что в режиме проверки типов по умолчаниюTТип(x: T) => voidявляется изменчивым, но в режиме строгой функциональной типизацииTустойчив к:
interface Comparer<T> {
compare: (a: T, b: T) => number;
}
declare let animalComparer: Comparer<Animal>;
declare let dogComparer: Comparer<Dog>;
animalComparer = dogComparer; // 错误
dogComparer = animalComparer; // 正确
Пишу здесь, вынужденный убить новичка.
Резюме и справка
Справочная статья:
На собеседованиях вас часто спрашиваютЗачемTypescriptСравниватьJavaScriptлегко использовать?
Из этих строгих правил режима вы можете получить представление о тайне,Строго открыто сегодня, и баги будут сбрасываться в будущем,О, да.
❤️ После прочтения трех вещей
Если вы найдете этот контент вдохновляющим, я хотел бы пригласить вас сделать мне три небольших одолжения:
- Ставьте лайк, чтобы больше людей увидело этот контент
- Обратите внимание на паблик «Учитель фронтенд-убеждения», и время от времени делитесь оригинальными знаниями.
- Также смотрите другие статьи
- Расширенное руководство по отладке Chrome Devtools (новое)
- 90 строк кода, 15 элементов для бесконечной прокрутки
- «React Hooks» 120 строк кода для реализации полного интерактивного компонента загрузки с помощью перетаскивания.
- «React Hooks» 160 строк кода для создания динамичных и крутых визуальных диаграмм - Ищут программиста
Вы также можете прийти ко мнеGitHubПолучите исходные файлы всех статей в блоге:
Руководство по убеждению:GitHub.com/Roger-Hi RO/…