0x00
前言
因为之前看过部分官方文档,故本文不是完整教程笔记。
0x01
环境搭建
-
基本使用:
- 安装Node.js
- 使用npm全局安装TypeScript
npm i -g typescript
- 编写ts文件
- 使用
tsc
编译tsc xxx.ts
-
自动编译文件:
编译文件时,使用
-w
指令后,TS编译器会自动监听文件的变化,并在文件内容发生变化时进行重新编译tsc file.ts -w
-
自动编译整个项目:
如果直接执行
tsc
指令,则可以自动将当前项目下的所有ts文件编译为js文件直接执行
tsc
命令的前提:在项目根目录下创建TS的配置文件tsconfig.json -
TS编译器配置文件tsconfig.json配置选项:
(注:tsconfig.json是“JSON with Comments”,可以写注释)
-
include
定义希望被编译文件所在的目录
路径中的
**
表示任意目录,*
表示任意文件-
默认值:
["**/*"]
-
示例:
"include": ["src/**/*", "tests/**/*"]
上述示例中,src目录和tests目录下的所有文件都会被编译
-
-
exclude
定义需要排除在外、不需要被编译文件的目录
-
默认值:
["node_modules", "bower_components", "jspm_packages"]
-
示例:
"exclude": ["./src/hello/**/*"]
上述示例中,src下hello目录下的文件都不会被编译
-
-
extends
定义被继承的配置文件
-
示例:
"extends": "./config/base"
上述示例中,当前配置文件中会自动包含config目录下base.json中的所有配置信息
-
-
files
指定被编译文件的列表,只有需要编译的文件少时才会用到
-
compilerOptions
编译器选项
JSON with Comments { "include": [ "./src/**/*" ], "compilerOptions": { // compilerOptions 编译器选项 "target": "es2015", // target 指定TS被编译后的ES版本,Argument for '--target' option must be: 'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021', 'es2022', 'esnext'. "module": "es2015", // module 指定要使用的模块化的规范,Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext', 'node16', 'nodenext'. // "lib": [] // lib 指定项目中要使用的库。有默认值,一般不需配置,如果设为空数组就会把默认的覆盖掉。Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021', 'es2022', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'webworker.iterable', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asyncgenerator', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.object', 'es2019.string', 'es2019.symbol', 'es2020.bigint', 'es2020.date', 'es2020.promise', 'es2020.sharedmemory', 'es2020.string', 'es2020.symbol.wellknown', 'es2020.intl', 'es2020.number', 'es2021.promise', 'es2021.string', 'es2021.weakref', 'es2021.intl', 'es2022.array', 'es2022.error', 'es2022.intl', 'es2022.object', 'es2022.string', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint', 'esnext.string', 'esnext.promise', 'esnext.weakref'. "outDir": "./dist", // outDir 用来指定编译输出文件的目录 // "outFile": "./dist/app.js" // 将代码合并为一个文件。Only 'amd' and 'system' modules are supported alongside --outFile // "allowJs": true, // 是否对js文件进行编译,默认是false // "checkJs": true, // 是否检查js代码是否符合语法规范,默认是false "removeComments": true, // 是否移除注释,默认为false // "noEmit": true // 不生成编译后的文件,只进行类型检查,默认为false "noEmitOnError": true, // 有错误时不生成编译后的文件,默认为false "strict": true // 所有严格检查的总开关(等效于下面四个选项),一般建议打开 // "alwaysStrict": true, // 编译后的文件是否使用严格模式,默认false // "noImplicitAny": true, // 不允许隐式的any类型 // "noImplicitThis": true, // 不允许不明确类型的this // "strictNullChecks": true // 严格检查空值 } }
-
一个细节:导入的模块要被使用才会被编译到js文件里
编译前的ts文件:
import { a } from 'ex.js'; console.log('c');
输出的js文件:
console.log('c'); export {};
-
-
TypeScript x Webpack
-
终端:
npm init # -D: 开发依赖,即--save-dev # ts-loader: 用于在Webpack中编译ts文件 npm i -D webpack webpack-cli typescript ts-loader
-
webpack.config.js:
module.exports = { module: { rules: [ //配置loader { test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/ } ] }, // 设置引用模块 resolve: { extensions: ['.ts', '.js'] //配置了之后,imoprt ts、js文件时,路径中的文件名不用后缀 } }
-
0x02
Basis
-
例:
let c: boolean = false;
-
如果变量的声明和赋值是同时进行的,TS可以自动对变量进行类型检测
故变量的声明和赋值同时进行时,一般不需写类型
let c = false;
而JS中的函数是不考虑参数的类型和个数的,故TS主要用于约束函数参数
0x03
Types
-
可以直接使用字面量进行类型声明
let a: 10;
-
可以使用
|
来连接多个类型(即联合类型)let b: 'male' | 'female'; let c: boolean | string;
-
TS中的类型:
类型 例子 描述 number
string
boolean
array
object
字面量 any
unknown
void
空值( undefined
)没有值(或 undefined
)never
没有值 不能是任何值 tuple
[4, 5]
元素,TS新增类型,固定长度数组 enum
enum{A, B}
枚举,TS中新增类型 -
any
表示的是任意类型,一个变量设置类型为any
后相当于关闭了TS对该变量的类型检测故使用TS时,不建议使用
any
类型声明变量如果不指定类型,则TS解析器会自动判断变量的类型为
any
(隐式的any
) -
unknown
表示未知类型的值any
类型的变量可以赋值给任意变量,即使赋给指定了类型的变量,TS也不会提醒unknown
类型变量赋值给其他类型的变量则TS会提醒,即使它的值的类型符合被赋值变量的类型
unknown
实际上就是一个类型安全的any
,因为unknown
类型的变量不能直接赋值给其他变量解决办法:
- 赋值前进行类型判断
- 类型断言:用来告诉解析器变量的实际类型
let u: unknown; u = true; u = 10; u = 'hello'; let a; a = true; a = 20; a = 'hi'; let s: string; //变量a的类型是any,可以赋值给任意变量,即使赋给指定了类型的变量,TS也不会提醒 s = a; u = 'hey'; //而unknown类型变量赋值给其他类型的变量则TS会提醒,即使它的值也是string类型 //s = u; //error TS2322: Type 'unknown' is not assignable to type 'string'. //处理方式:赋值前进行类型判断 if (typeof u === 'string') { s = u; } /* 类型断言 语法: 变量名 as 类型; <类型>变量名 */ s = u as string; s = <string>u;
-
never
表示永远不会返回结果,用于抛错误function fn(): never { throw new Error('Error!'); }
-
object
表示对象,由于过于宽泛,一般不用通常用
type obj = { 属性名1: '属性值', 属性名2: '属性值' }
方式来指定对象中包含哪些属性
[propName: string]: any
表示任意类型的属性:type objType = { name: string, [propName: string]: any }; let obj: objType = { name: '王德发', age: 18, gender: '男' };
-
函数结构的类型声明:
-
type 类型名 = (形参: 类型, 形参: 类型, ...) => 返回值类型
-
let 函数名 = (形参: 类型, 形参: 类型, ...): 返回值类型 => 返回值或函数体
-
function 函数名(形参: 类型, 形参: 类型, ...): 返回值类型 { 函数体 }
type addNumType = (p1: number, p2: number) => number; let addNum: addNumType = (a, b) => a + b; let concatStr = (a: string, b: string): string => a + b; let addResult = addNum(1, 2); let concatResult = concatStr('1', '2'); console.log({ addResult, concatResult }); //{ addResult: 3, concatResult: '12' }
(参数名可以随便写)
-
-
数组的类型声明:
类型[]
Array<类型>
let strArr: string[] = ['1', '2', 3]; //error TS2322: Type 'number' is not assignable to type 'string'. let numArr: Array<number> = [1, 2, '3']; //error TS2322: Type 'string' is not assignable to type 'number'.
-
元组:即固定长度的数组
一般长度比较有限(长的话一般用
Array
),但性能会高些语法:
[类型, 类型, 类型]
type tupleType = [ string, number ]; let h: tupleType = [ 'hello', 123, 456 ]; //error TS2322: Type '[string, number, number]' is not assignable to type 'tupleType'. Source has 3 element(s) but target allows only 2.
-
enum
: 枚举可以不指定具体的值
//可以不指定具体的值 enum Gender { male, female } //也可以指定具体的值 enum Province { guangdong = '广东', guangxi = '广西' } type Human = { name: string, gender: Gender, province: Province }; let wong: Human = { name: '王德发', gender: Gender.male, province: Province.guangdong }; let hou: Human = { name: '侯丽榭', gender: Gender.female, province: Province.guangxi }; console.log(wong, hou); //{ name: '王德发', gender: 0, province: '广东' } { name: '侯丽榭', gender: 1, province: '广西' }
-
对象模式组合
&
: 与type Human = { name: string } & { age: number } let wong: Human = { name: '王德发' }; //error TS2322: Type '{ name: string; }' is not assignable to type 'Human'. Property 'age' is missing in type '{ name: string; }' but required in type '{ age: number; }'.
-
类型别名
let a: 1 | 2 | 3 = 1; let b: 1 | 2 | 3 = 2; type TypeA = 4 | 5 | 6; let d: TypeA = 5; let c: TypeA = 6; let e: TypeA = 7; //error TS2322: Type '7' is not assignable to type 'TypeA'.
0x04
类
-
readonly
属性修饰符只读属性
声明静态只读属性时须置
readonly
于static
之后readonly name: string = '王德发' static readonly num: number = '60 billion'
-
abstract
修饰符抽象类、抽象方法
abstract class Animal { abstract sayHello(): void; }
-
type
描述对象的类型
type myType = { name: string, //逗号分隔 age: number }; //error TS2300: Duplicate identifier 'myType'. type myType = { gender: string }; //error TS2300: Duplicate identifier 'myType'.
-
interface
定义类中应该包含哪些属性和方法,同时也可当成类型声明去使用
interface myInterface { name: string; age: number; sayHello(): void; } interface myInterface { //interface可以多次定义 gender: string; } const obj: myInterface = { //作为类型声明来约束对象 name: 'a', age: 3, gender: '未知', //定义结果取interface多次定义的并集 sayHello: () => {} } console.log(obj); // { name: 'a', age: 3, gender: '未知', sayHello: [Function: sayHello] } //作为接口来约束类 class MyClass implements myInterface { name = '默认名'; age = 18; gender = '男'; sayHello() { console.log('你好'); } }
-
type
与interface
的区别:type
不可重复定义,但interface
可以,结果取并集type
内用逗号分隔属性,interface
内用分号分隔属性
-
接口与抽象类的区别:
- 接口中所有方法都是抽象方法
- 抽象类中的方法既可抽象又可具体
-
属性的封装
TS中的属性修饰符:
public
private
: 只能在类内部进行访问(及修改),子类亦无法访问protected
: 只能在当前类及其子类中访问(及修改)
class Person { name: string; private _age: number; constructor(name: string, age: number) { this.name = name; this._age = age; } getAge(pwd: string) { return pwd === 'password' ? this._age : undefined } } const wong = new Person('王德发', 20); wong.name = '侯丽榭'; // console.log(wong._age); //error TS2341: Property '_age' is private and only accessible within class 'Person'. console.log(wong.getAge('password')); //20 // wong._age = 80; //error TS2341: Property '_age' is private and only accessible within class 'Person'.
-
可以直接将属性定义在构造函数中
下例中的Person类等效于上例中的Person类:
class Person { //将属性定义在构造函数中 constructor( public name: string, //这里的public必须写,否则只会被当成构造函数的形参而非类的实例属性 private _age: number ) { this.name = name; this._age = _age; } getAge(pwd: string) { return pwd === 'password' ? this._age : undefined } } const wong = new Person('王德发', 20); wong.name = '侯丽榭'; // console.log(wong._age); //error TS2341: Property '_age' is private and only accessible within class 'Person'. console.log(wong.getAge('password')); //20 // wong._age = 80; //error TS2341: Property '_age' is private and only accessible within class 'Person'.
0x05
泛型
-
函数与泛型
function fn<T>(p: T): T { return p; } // 可以不指定类型、直接调用具有泛型的函数,TS可以自动对类型进行推断 let result = fn(10); console.log(typeof result, { result }); // number { result: 10 } let result2 = fn<string>('hello'); console.log(typeof result2, { result2 }); // string { result2: 'hello' } // 指定多个泛型 function fn2<T, K>(p1: T, p2: K): T { console.log(typeof p2, { p2 }); // string { p2: 'hello' } return p1; } let result3 = fn2<number, string>(123, 'hello'); console.log(typeof result3, { result3 }); // number { result3: 123 }
-
extends
interface Inter { length: number; } // <T extends Inter> 表示泛型T必须是接口Inter的实现类(子类) function fn<T extends Inter>(p: T): number { return p.length; } // fn(1111); // error TS2345: Argument of type 'number' is not assignable to parameter of type 'Inter'. console.log(fn('1111')); // 4 console.log(fn([1, 2, 3, 4, 5])); // 5
-
类与泛型
class MyClass<T> { name: T; constructor(name: T) { this.name = name; } } const s = new MyClass<string>('王德发'); const n = new MyClass<number>(114514); console.log(typeof s.name, s); // string MyClass { name: '王德发' } console.log(typeof n.name, n); // number MyClass { name: 114514 }
//End of Article