предисловие
«Мама, я хочу писать на 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/…