Review
- 2020-08-26
- 2025-04-04 14:34
[!Summary]
一、Introduction #
微软开发的开源编程语言。于2012年10月发布首个公开版本; 目前大型项目的标配
TS主要为了实现2个目标:
- 为JS提供可选的类型系统;能提高代码质量、可读性和可维护性;有利于在编译时而不是运行时捕获错误;
- 是JS的超集;兼容当前及未来的JS特性
DefinitedTyped社区定义了大多数流行的JS库的TypeScript声明模板,安装使用@types
declare关键字告诉TS,你正在试图表述一个在其他地方已经存在的代码;
对于没有声明的模块及变量的快速修复方法:globals.d.ts
declare var $:any; // 全局变量
declare let process: any;
declare const myPoing: { x: number; y: number; };
declare module 'foo' {}; // 模块
declare type JQuery = any; // 在某些内容上添加显示的注解,并且会在类型声明空间使用它;
declare module '*.css';编译上下文 #
tsconfig.json
声明空间 #
- 类型声明空间:用来当做类型注解;
class,interface,type - 变量声明空间:用来当做变量的内容;
class
模块 #
- 全局模块
- 文件模块(外部模块):在文件中含有import或export,那么文件中就创建了一个本地作用域。
根据 tsconfig 中 module 选项,把TypeScript编译成不同的JavaScript模块类型
- AMD:只能在浏览器工作
- SystemJS:这是一个好的实验,已经被ES模块代替。
- ES模块:还未准备好
- CommonJS:这个选项比较好;
模块路径 #
- 相对模块路径
- 动态查找模块
动态查找模块foo的顺序: #
./node_modules/foo../node_modules/foo../../node_modules/foo- 一直查到系统的根目录
被检查的place,TS将会检查一下内容:
- place表示一个文件,如
foo.ts - place是一个文件夹,并且存在一个文件
foo/index.ts - place是一个文件夹,存在
foo/package.json文件,其中指定了 types 的文件路径 - place是一个文件夹,存在
foo/package.json文件,其中指定了 main的文件路径
懒加载 #
require可以实现运行时加载文件。 所以应该在类型注解中使用导入的模块名称,在代码被编译成JS时,这些将会被移除; 应用场景:
- Web App里,在特定路由上加载JS时
- 在Node应用里,只想加载特定模块,用来加快启动速度时;
命名空间 #
namespace Utility {
export function log(msg){}
export function error(msg) {}
}
Utility.log(‘test’);
Utility.error(‘test’);命名空间支持嵌套。
动态导入表达式 #
import() proposal for javascript
webpack可以通过此方法实现代码分割
require.ensure(不赞成使用了,非标准方法)类型系统 #
- 基本类型注解:string, number, boolean
- 数组注解:
number[] - 接口注解:多个类型注解,合并成一个类型注解;
interface Name { first: string; second: string; } - 内连类型注解:使用
let;let name: {first: string; second: string;}省去了为类型起名的麻烦;如果使用频繁,需要重构为接口或type alias - 特殊类型:
- any:关闭类型检查,尽量不要使用;
- null,undefined:可以被赋值给任意类型的变量
- void:一个函数没有返回值
- 泛型:
interface Array<T> { reverse(): T[]; }; function a<T> (items: T[]): T[]{} - 联合类型注解:使用 | 作为类型注解;
- 交叉类型:extends是一种非常常见的模式,可以根据两个对象创建一个新对象,新对象拥有两个对象所有的功能;
- 元祖类型:
let nameNumber: [string, number] - 类型别名:
type SomeName = someValidTypeAnnotation
可以为任意类型注解提供类型别名;
交叉类型: #
function extend<T, U>(fiist: T, second: U): T & U {}如果需要使用类型注解的层次结构,使用接口;它能使用implements和extends;类可以实现接口,接口可以自己实现继承; 当想给联合类型或交叉类型提供一个语义化的名称时,使用类型别名更好。
function a<T extends string>(o: Array<T>):{[K in T]: K}{}可以使用 keyof, typeof 来生成字符串的联合类型;
const B = a(['North', 'South']);// 创建一个类型
type B = keyof typeof B;
let c: B;
c = B.North;
c = ’North’;类型断言 #
<string>foo: 不推荐,跟React一起使用时,有歧义;- as:
foo = bar as any;类型断言之所以不被称为“类型转换”,是因为转换通常意味着某种运行时的支持。但是类型断言纯粹是一个编译时的语法。 尽量少用断言; 双重断言:
const element = (event as any) as HTMLElement;枚举 #
枚举是组织收集相关联变量的一种方式;枚举类型的值默认都是数字类型的,也称为数字枚举;
enum Card {
red,
green,
yellow,
}
enum Book {
A = ‘a’,
B = ‘b’,
}应用场景时做标记,检查条件是否为真。配合|&~;
- |=:添加标记
- &=和~:清除标记
- |:合并标记
常量枚举会有一个性能提升:
const enum TTTT {
TRUE,
}const lie = TTTT.TRUE; // 将会被编译成 const lie = 0;
可以使用enum + namespace的声明方式向枚举类型添加静态方法;需要定义相同的标识符;
安装TS会顺带安装一个lib.d.ts声明文件,该文件包含JS运行时DOM中的各种常见JS环境声明,如window,document,math,Window,Document; lib分类如下: JavaScrpt功能
- es5
- es6
- es2015
- es2016
- es2017
- ES2018
- ES2019
- ES2020
- ESNext
运行环境:
- dom
- dom.iterable
- webworker
- WebWorker.ImportScripts
- ScriptHost
函数可选参数:function foo(name?: string): void {} 函数重载:需要多次声明函数头,最后一个函数头是实现函数,需要与所有重载兼容; 函数类型定义: 1)接口定义
interface Complex {
(foo: string): stirng;
(foo: number): number;
}可实现重载;
2)箭头函数
(foo: number) => string;
不能实现重载;
3)可实例化
interface Test {
new () : string;
}意味着你需要使用new关键字去调用它。
Freshness 更严格的对象字面量类型检查 之所以只对对象字面量类型检查,因为实际上那些并没有被用到的属性有可能会拼写错误或被误用; 典型用例:React state;
readonly标记对象属性;可以在接口和类型别名里面使用;
变体 类型兼容性的分析;
- 协变
- 逆变
- 双向协变
- 不变
never(是底部类型) 永远不会发生的事情
- 一个从来不会有返回值的函数
- 一个总会抛出错误的函数
类型移动 #
目的:无缝与JS高动态语言一起工作; 关键动机是,当改变了一个内容时,其他相关的内容都会自动更新;
- 复制类型和值:
namespace importing{ export class Foo{}}; import Bar = importing.Foo; let bar: Bar; - 捕获变量类型:(变量)
let foo =123; let bar: typeof foo; - 捕获类成员类型:
class Foo { foo: number }; declare let _foo: Foo; let bar: typeof _foo.foo; - 捕获字符串类型:(常量)
const foo = ''; let bar: typeof foo; - 捕获键的名称:
keyof typeof
ThisType #
可以在对象字面量中输入this;
非空断言操作符:!
e!.name; // 断言e是非null;
配合Jest测试库
jest, @types/jest, ts-jest(ts预处理器), enzyme, @types/enzyme, enzyme-to-json, enzyme-adapter-react-16
Prettier, husky, conventional-changelog, standard-version;
eslint eslint-plugin-react @typescript-eslint/parser @typescript-eslint/eslint-plugin谨慎使用 — outFile 对于大部分使用者来说,命名空间可以用模块来替代。 Barrel就像一个容器,他的作用就是把分散在多个模块的导出合并到一个模块里。
代码风格与代码约定 #
- camelCase命名:变量,函数名,类属性方法,接口成员,
- PascalCase命名:类名,接口,类型别名,命名空间,枚举类型
- 单引号
- 2空格缩紧
- 使用分号
- 需要联合类型或交叉类型,使用type
- 想用extends或implements,使用interface
TypeScript编译原理 #
- Scanner扫描器
- Parser解析器
- Binder绑定器
- Checker检查器
- Emitter发射器
Reference #
- 《深入浅出TypeScript》
- tsconfig: https://www.typescriptlang.org/tsconfig