TypeScript 是什么
TypeScript
TypeScript: JavaScript With Syntax For Types. (typescriptlang.org)
TypeScript中文网 · TypeScript——JavaScript的超集 (tslang.cn)
TypeScript是JavaScript的超集,是一个可选的、静态的类型系统。
在JS的基础上,增加的是类型系统。2012年微软发布。开源、拥抱ES标准。
- 类型系统
对代码中所有的
标识符(变量、函数、参数、返回值)进行类型检查。
- 可选的
学习曲线非常平滑。
- 静态的(在运行之前)
类型检查发生的时间,在编译的时候,而非运行时,TS不参与任何运行时的类型检查。
- 安装tsc:
ts编译器
无论是浏览器环境,还是node环境,无法直接识别ts代码,
tsc: ts ==> es。
$ npm i -g typescript
Version 4.6.4
默认情况下,TS会做出下面几种假设:
假设当前的执行环境是
dom。如果代码中
没有使用模块化语句(import、export),便认为该代码是全局执行。编译的目标代码是
ES3。
-
有两种方式更改以上默认情况
- 使用
tsc命令行的时候,加上选项参数。 - 使用
ts配置文件,更改编译选项。
- 使用
TS的配置文件
- 配置文件初始化,命令行执行
tsc --init,当前路径下会生成tsconfig.json
{
"compilerOptions": {
//编译选项
"target": "es2016", //配置编译目标代码的版本标准
"module": "es6", //配置编译目标使用的模块化标准
"lib": ["es2016"], // 开发环境
"outDir": "./dist"
},
"include": ["./src"] // 设置需要进行编译的文件,支持路径模式匹配;
}
src/index.ts
import num from './test'
const a: string | number = num
src/test.ts
const num = 999
export default num
- 命令行执行
tsc
dist目录生成了编译后的代码。
- 开发环境中配置node环境
npm i -D @types/node
@types是一个ts官方的类型库,其中包含了很多对JS代码的类型描述。
注意:
使用了配置文件后,使用tsc进行编译时,不能跟上文件名,如果跟上文件名,会忽略配置文件。
tsconfig.json
- 重要选项
files - 设置要编译的文件的名称;
include - 设置需要进行编译的文件,支持路径模式匹配;
exclude - 设置无需进行编译的文件,支持路径模式匹配;
compilerOptions - 设置与编译流程相关的选项。
compilerOptions选项
{
"compilerOptions": {
/* 基本选项 */
"target": "es5", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
"module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
"lib": [], // 指定要包含在编译中的库文件
"allowJs": true, // 允许编译 javascript 文件
"checkJs": true, // 报告 javascript 文件中的错误
"jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
"declaration": true, // 生成相应的 '.d.ts' 文件
"sourceMap": true, // 生成相应的 '.map' 文件
"outFile": "./", // 将输出文件合并为一个文件
"outDir": "./", // 指定输出目录
"rootDir": "./", // 用来控制输出目录结构 --outDir.
"removeComments": true, // 删除编译后的所有的注释
"noEmit": true, // 不生成输出文件
"importHelpers": true, // 从 tslib 导入辅助工具函数
"isolatedModules": true, // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).
/* 严格的类型检查选项 */
"strict": true, // 启用所有严格类型检查选项
"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
"strictNullChecks": true, // 启用严格的 null 检查
"noImplicitThis": true, // 当 this 表达式值为 any 类型的时候,生成一个错误
"alwaysStrict": true, // 以严格模式检查每个模块,并在每个文件里加入 'use strict'
/* 额外的检查 */
"noUnusedLocals": true, // 有未使用的变量时,抛出错误
"noUnusedParameters": true, // 有未使用的参数时,抛出错误
"noImplicitReturns": true, // 并不是所有函数里的代码都有返回值时,抛出错误
"noFallthroughCasesInSwitch": true, // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)
/* 模块解析选项 */
"moduleResolution": "node", // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
"baseUrl": "./", // 用于解析非相对模块名称的基目录
"paths": {}, // 模块名到基于 baseUrl 的路径映射的列表
"rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的结构内容
"typeRoots": [], // 包含类型声明的文件列表
"types": [], // 需要包含的类型声明文件名列表
"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。
/* Source Map Options */
"sourceRoot": "./", // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
"mapRoot": "./", // 指定调试器应该找到映射文件而不是生成文件的位置
"inlineSourceMap": true, // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
"inlineSources": true, // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性
/* 其他选项 */
"experimentalDecorators": true, // 启用装饰器
"emitDecoratorMetadata": true // 为装饰器提供元数据的支持
}
}
基本类型约束
基本类型
- number
const num: number = 100
-
string
-
boolean
-
null、undefined
null和undfined是所有其他类型的子类型,它们可以赋值给其他类型。通过添加
strictNullChecks: true,可以获得更严格的空类型检查,null和undefinde只能赋值给自身。
- 数组
const arr: number[] = [3, 4, 5]
- object
const oK: object = {
name: 'Kobe',
No: '24'
}
其他类型
- 类型别名
type Gender = {male: '男'; female: '女'}
const gender: Gender = {
male: '男',
female: '女'
}
- 联合类型
多种类型任选其一,配合类型保护进行判断。
类型保护: 当对某个变量进行类型判断之后,在判断的语句块中便可以确定他的确切类型。
typeof可以触发类型保护。
const user: string | undefined = 'Kobe'
- void 类型
通常用于约束函数的返回值,表示该函数没有任何返回。
const fn = (): void => {}
- never 类型
通常用于约束函数的返回值,表示该函数永远不可能结束。
const fn = (): never => {
throw new Error()
}
- 元组类型(Tuple)
一个固定长度的数据,并且数据中的每一项的类型确定。
// 元祖的表示和数组非常类似,只不过它将类型写在了里面 这就对每一项起到了限定的作用
let user: [string, number] = ['viking', 20]
- any类型
any类型可以绕过类型检查,因此,any类型的数据可以赋值给任意类型。
- 字面量类型
使用一个值进行约束。
type No = 24
const num: No = 24 // 只能为24
函数的相关约束
- 函数重载
在函数实现之前,对函数调用的多种情况进行声明。
function sum(a: number, b: number): number
function sum(a: string, b: string): string
function sum(a: number | string, b: number | string) {
if (typeof a == 'number' && typeof b == 'number') {
return a + b
}
if (typeof a == 'string' && typeof b == 'string') {
return a + b
}
}
console.log(sum(1, 2)) // 3
console.log(sum('a1', 'a2')) // a1a2
- 可选参数
可以在某些参数名后加上问好,表示该参数可以不用传递。可选参数必须在参数列表的末尾。
function sum(a: number | string, b: number | string, c?: number) {
if (typeof a == 'number' && typeof b == 'number') {
return a + b + c
}
if (typeof a == 'string' && typeof b == 'string') {
return a + b + c
}
}
console.log(sum(1, 2, 3)) // 6
console.log(sum('a1', 'a2')) // a1a2undefined
扩展类型
什么是扩展类型?
自己开发的类型。
扩展类型:类型别名、枚举、接口、类
枚举
- 定义
枚举会出现在编译结果中,
编译结果中表现为对象。
enum 枚举名 {
字段1=值1
字段2=值2
}
- 规则
枚举的字段值可以是
字符串或数字;数字枚举的值会自动自增。被数字枚举约束的变量,可以直接赋值为数字。
数字枚举的编译结果和字符串枚举有差异。
- 最佳实践
尽量不要在一个枚举中即出现字符串字段,又出现数字字段。
使用枚举时,尽量使用枚举字段的名称,而不是用真实的值。
enum Gender {
male = '男',
female = '女'
}
let gender: Gender = Gender.male
gender = Gender.female
interface
TypeScript 的接口:用于约束类,对象、函数的标准。
- 接口约束对象
interface User {
name: string
age: number
}
let user: User = {name: 'oK', age: 11}
- 约束函数
- 对象中的函数
interface User {
name: string
sayHello: () => void
}
let user: User = {
name: 'oK',
sayHello: () => {}
}
- 直接约束函数
interface conditon {
//定义函数的参数类型和返回值类型
(n: number): boolean
}
function sum(numbers: number[], callBack: conditon) {}
const callback = (n: number) => {
return true // 返回非 boolean类型,会报错提醒
}
sum([1, 2], callback)
- 接口可以继承
- 可以通过接口之间的继承,实现多种接口的组合
interface A {
name: string
}
interface B {
number: number
}
interface C extends A, B {}
const a: C = {
name: 'oA',
number: 9 // C继承了A、B的属性约束
}
- 使用类型别名可以实现类似的 组合效果,需要通过 & ,他叫做
交叉类型
type A = {name: string}
type B = {number: number}
type C = A & B
const a: C = {
name: 'oA',
number: 9 // 使用交叉类型
}
-
他们的区别
-
- 定义基本类型别名
type可以定义基本类型别名, 但是interface无法定义,如:
type userName = string
const user: userName = 'mj'
-
- 声明合并
如果你多次声明一个同名的接口,TypeScript 会将它们合并到一个声明中,并将它们视为一个接口。这称为声明合并, 例如:
interface Person {
name: string
}
interface Person {
age: number
}
let user: Person = {
name: 'Tolu',
age: 0
}
这种情况下,如果是
type的话,重复使用Person是会报错的:
type Person { name: string };
// Error: 标识符“Person”重复。ts(2300)
type Person { age: number }
- readonly修饰符
只读修饰符,修饰的目标是只读的。
不在编译结果中。
interface User {
readonlyid: string
name: string
readonlyarr: readonly string[] // 限制arr 不能重新赋值和不能改变。
}
const arr: readonly number[] = [3, 4, 5]
// Error: 类型“readonly number[]”上不存在属性“push”。ts(2339)
arr.push(999)
TS中的类
- 属性列表的方式
class Animal {
name: string
age: number
constructor(name: string, age: string) {
this.name = name
// Error: 不能将类型“string”分配给类型“number”。ts(2322)
this.age = age
}
}
- 属性的初始化检查
更加严格的方式检查属性是不是初始化。
属性的初始化位置: 构造函数中、属性默认值
"stricPropertyInitialization": true
- 类成员的访问修饰符
public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的。
private 修饰的属性或方法是私有的,不能在声明它的类的外部访问。
protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的。
- 访问器
getter && setter
class Parent {
private _name: string
get name(): string {
return this._name
}
set name(name: string) {
console.log(`name设置前: ${this._name} 设置后: ${name}`)
this._name = name
}
}
const parent = new Parent()
parent.name = 'ok' // 可以直接使用赋值语句,但是会自动调用set name(name: string)方法
- 类与接口
类实现一个接口。
泛型
泛型是指附属于函数、类、接口、类型别名之上的类型。
泛型相当于一个类型变量,在定义是,无法预先知道具体的类型,可以用该变量来代替,只有到调用时,才能确定他的类型。
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
函数中使用泛型
在函数名之后写上
<泛型名称>
function echo<T>(arg: T): T {
return arg
}
const num = echo<number>(123)
console.log(typeof num) // number
const str = echo<string>('123')
console.log(typeof str) // string
- 在
类、接口、类型别名中使用泛型
直接在名称后面写上
<泛型名称>
- 类中使用
class Queue<T> {
private data = []
push(item: T) {
return this.data.push(item)
}
pop(): T {
return this.data.shift()
}
}
const queue = new Queue<number>()
queue.push(1)
// Error: 类型“string”的参数不能赋给类型“number”的参数。ts(2345)
queue.push('1')
- interface 中使用
interface KeyPair<T, U> {
key: T
value: U
}
let kp1: KeyPair<number, string> = {key: 1, value: 'str'}
let kp2: KeyPair<string, number> = {key: 'str', value: 123}
- 泛型约束
用于
限制泛型的取值。在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法。
interface IWithLength {
length: number
}
function echoWithLength<T extends IWithLength>(arg: T): T {
console.log(arg.length)
return arg
}
echoWithLength('str') // 3
echoWithLength({length: 10}) // 10
echoWithLength([1, 2, 3]) // 3
多泛型
// 泛型也可以传入多个值
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]]
}
const result = swap<string, number>(['string', 123])
console.log(result)