README
前言
学习你需要知道的关于 TypeScript 的所有东西。
"use strict"
指令在 JavaScript 1.8.5 (ECMAScript5)
中新增。
至今,前端er们基本都默认开启严格模式敲代码。
那么,你知道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
解决方案有四种。