文中示例可直接在 playground 上运行。
boolean string number enum undefined null Array tuple any unknown void never
const isRich: boolean = true
const name: string = "Tony"
const age: number = 18
console.log(typeof isRich)
console.log(typeof name)
console.log(typeof age)
enum Animal {
CAT,
DOG
}
const animal: Animal = Animal.DOG
console.log(animal) // 打印 1
const 修饰修饰枚举在ts转换成js时,不会保留枚举的特性,可在playground验证。
const enum City {
SH = "shanghai",
BJ = "chengdu",
SZ = "shenzhen"
}
const shanghai: City = City.SH
console.log(shanghai)
默认情况下,可以把 undefined 和 null 赋值给其他类型,
但是再配置文件tsconfig.json中 配置 “strictNullChecks”:true后则不可以了
// 开启了 "strictNullChecks":true
let u: undefined = undefined
let n: null = null
// const name: string = u // 报错
// u = n // 不能相互赋值
// undefined 可以赋值给 void
// null 不能复制给void
const a: void = u
// const b: void = n // 会报错
元组(tuple) 规定了已知长度和类型的数组。
// 普通的数组
const arr: string[] = ['cat', 'dog']
//等价于
const arr1: Array<string> = ['cat', 'dog']
// tuple元组是严格的数组
const xuanyu: [string, number, string] = ['炫羽', 18, '男']
void常用在函数上,表明函数没有返回值。
function fn(): void {
console.log('炫一个?')
}
fn()
never表示永远也不会执行完的函数。
function fn(): never {
while(true){}
}
// 抛出异常
function errFn(): never {
throw new Error()
}
any: 可以跳过类型检查,任何类型都可以复制给any。
// any -> 任何类型赋值给any
const MyName: any = '炫羽'
const myMoney: any = 100000000
const isAnimal: any = true
// any -> any赋值给其他任何类型
const newName: string = MyName
const dog: boolean = isAnimal
unknown: 和any一样。区别unknown只能赋值给unknown和any。
// any -> 任何类型赋值给any
const MyName: unknown = '炫羽'
const myMoney: unknown = 100000000
const isAnimal: unknown = true
// any -> any赋值给其他类型
// const newName: string = MyName // 报错
const money: unknown = 100000000 // 正确
const dog: unknown = isAnimal // 正确
函数 对象 数组 类
函数是用的非常多的类型了。
基本 - 用法
const fn: () => void = () => {
console.log('666')
}
默认参数 - 用法
function fn(age: number = 18):void {
console.log(age)
}
fn()
可选参数 - 用法
function sum(a: number, b: number = 1, c?: number) {
if(c) {
return a + b + c
}
return a + b
}
console.log(sum(1,2)) // 3
console.log(sum(1,2,3))// 6
剩余参数(必须是一个数组类型)
function sum(...numbers: number[]) {
let res = 0
for(let i = 0; i < numbers.length; i++) {
res += numbers[i]
}
return res
}
console.log(sum(1,2)) // 3
console.log(sum(1,2,3))// 6
let a: object = {}
let b: object = { name: '炫羽' }
let c: {}
c = { name: '炫羽' }
console.log(c)
// 普通的数组
const arr: string[] = ['dog', 'cat']
//等价于
const arr1: Array<string> = ['dog', 'cat']
class People {
name: string
age: number
constructor(name:string, age: number) {
this.name = name
this.age = age
}
info() {
console.log('名字:' + this.name)
console.log('年龄:' + this.age)
}
}
一般首字母大写。接口类型的需要声明全部的属性或方法。
简单的例子
interface Person {
name: string
age: number
speak: () => void
}
const lisi: Person = {
name: '李四',
age: 18,
speak() {
console.log('666')
}
}
可选属性、只读属性
可选属性用 *?*表示。
只读属性用 readonly。
interface Person {
name?: string
readonly age: number
}
const lisi: Person = {
// name: '李四', // 可选 可以定义也可以不定义
age: 18
}
// readonly 修饰 只读
// lisi.age = 19 // 报错
鸭式辨型法
只要传入的对象包含了接口的部分属性或者方法(鸭子的特征)那么可以认定传入的参数实现了这个接口。
interface nameValue {
name: string;
}
function getNmae(nameObj: nameValue) {
console.log(nameObj.name);
}
let lisi = { age: 18, name: "我是李四" };
getNmae(lisi); // 通过
// getNmae({ age: 18, name: "我是李四" }); // 报错
仅需满足一个
let a: string | number = 1
a = 2 // 正确
a = 'lisi' // 正确
需要同时满足
interface a {
name: number
age: number
}
interface b {
name: string
city: string
}
let c: (a & b) = {
name: '炫羽',
age: 18,
city: '成都'
}
ts会尝试推断其类型。推断不出时则为any类型。
const myName = "lisi" // 推断出 myName 是 string类型
手动指定类型,as 来指定类型.
const Myname = "abcdefg"
const count1 = (Myname as string).length
相当于给类型重新起一个名字。
interface a {
name: number
say: () => void
}
type str = string
type num = number
type b = a // b 是 a类型的别名
当传进来的值的类型不止一种的时候,需要对值进一步的判断,确保值类型在可控的范围内。
in 关键字
可以判断 某个 key 在不在其中。
interface a {
name: number
age: number
}
interface b {
name: string
city: string
}
function fn(param: a | b) {
if('age' in param)
console.log(param.age)
if('city' in param)
console.log(param.city)
}
typeof 关键字
和js中的typeof类似,只能用于基本类型
let x: string= "字符串不可以"
console.log(typeof x === 'string')
let y: number= 3
console.log(typeof y === 'number')
instanceof 关键字
instanceof 和 typeof 的区别是:instanceof 可以用于对象,且子类判断基类也是 true
class A{
a:number
constructor(a1:number){
this.a = a1
}
}
class B extends A{
b:string
constructor(a1:number, b1:string){
super(a1)
this.b = b1
}
}
let bbb = new B(30, "aaa")
console.log(bbb instanceof A)
static 修饰的变量/属性是挂载在类本身,实例化后不能通过this访问到static修饰的成员。
单例模式
class Demo {
private static instance: Demo
public name: string
// 构造函数私有化,就相当于不能用 new 实例化
private constructor(name: string) {
this.name = name
}
//只能通过这一句来实例化(限制单例)
public static getInstance() {
// 限制单例
if(!this.instance) {
this.instance = new Demo("hello")
}
return this.instance
}
}
const demo1 = Demo.getInstance()
const demo2 = Demo.getInstance()
console.log(demo1 === demo2) // true
类似于interface的功能,abstract 修饰之后变成抽象方法,抽象类只能被继承。
abstract class Gemo {
constructor(public width: number, height: number ) { }
abstract getArea(): number
public setWidth(width:number) {
this.width = width
}
}
class Circle extends Gemo {
public getArea(): number {
return this.width
}
}
// interface 可以继承 类似于抽象类相同的功能 将相同的提取出来
常见泛型变量代表的意思:
interface Item {
name: string
}
class DataManager<T> {
constructor(private data: T[]) {}
public getData(index: number) {
return this.data[index]
}
}
const dataArr = new DataManager([{name: "lisi"}])
console.log(dataArr.getData(0))
interface KeyValue<K, V> {
key: K;
value: V;
}
泛型表示任意类型,当在某处调用了该值下的一个属性,但是该类型是不一定有该属性,所以会发生不可预期的错误,如:
function fn<T>(param: T): void {
// param 不知道是什么具体的解构
// console.log(param.length)
// 所以需要增加一些工具来判断参数是什么结构
}
为了更好的约束泛型,使用一些工具来增强泛型的可读性。
可以获取变量、属性、函数的类型。
interface Person {
name: string
age: number
getName: () => string
}
const xuanyu: Person = {name: '李四', age: 18, getName: () => '炫羽'}
type Teacher = typeof xuanyu // 类型 Teacher = 类型 Person
type Fn = typeof xuanyu.getName // 此处 fn是函数类型:() => string
const getNickname: Fn = () => {
return '死亡一刀斩'
}
用来遍历枚举类型。
enum Words {
A,
B,
C
}
type AliasWords = {
[w in Words]: string
}
// AliasWords 的类型等价为
// type AliasWords = {
// 0: string;
// 1: string;
// 2: string;
// }
同时也是 es6 里引入的一个新的关键字,而且不同场景下代表的含义不一样,特此总结一下:
class Animal {
kind = 'animal'
constructor(kind){
this.kind = kind;
}
sayHello(){
console.log(`Hello, I am a ${this.kind}!`);
}
}
class Dog extends Animal {
constructor(kind){
super(kind)
}
bark(){
console.log('wang wang')
}
}
const dog = new Dog('dog');
dog.name; // => 'dog'
dog.sayHello(); // => Hello, I am a dog!
interface Animal {
kind: string;
}
interface Dog extends Animal {
bark(): void;
}
// Dog => { name: string; bark(): void }
interface Type1<T extends string> {
name:T,
age:number
}
let aaa:Type1<string> = {
name:"泛型约束",
age:18
}
type Human = {
name: string;
}
type Duck = {
name: string;
}
type Bool = Duck extends Human ? 'yes' : 'no'; // Bool => 'yes'
获取类型的所有键,返回的是键的联合类型。
interface Person {
name: string
age: number
getName: () => string
}
type key1 = keyof Person // 'name' | 'age' | 'getName'
type key2 = keyof Person[] // 数组的键 比如:'length' | 'toString' | 'pop' | 'push' | 'concat' | 'join'
type key3 = keyof string // string类型的键 比如:number | 'toString' | 'charAt' | 'charCodeAt' | 'concat'...
let aaa:Person[key1] = "你的名字"
let bbb:Person[key1] = 18
// 当是索引类型的时候
type key4 = keyof { [k: string]: boolean } // string | number
type key5 = keyof { [k: number]: boolean } // number
function prop(obj, key) {
return obj[key];
}
函数中obj和key是不确定的,所以是不能确定是否可以执行 obj[key] 这个操作。
进一步优化:
function prop(obj: object, key: string) {
return (obj as any)[key];
}
很显然加上 any 虽然可以解决燃眉之急,但这任然不是一个好的方案,还是不能确定是否可以执行 obj[key] 这个操作。用keyof可以解决这个问题:
function prop<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
不管obj中有什么属性,key总能找到对应键值。
interface Person {
name: string
age: number
}
const xuanyu: Person = {
name: '炫羽',
age: 18
}
function prop<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const myName = prop(xuanyu, 'name') // myName: string
// const myName = prop(xuanyu, 'nickname') // 传入不存在的键值,则报错
在条件类型语句中, infer 推断一个类型变量。
infer语法的限制如下:
//1.简单使用,该语句仅仅展示用法,无实际意义
type InferSelf<T> = T extends infer U ? U : never;
type T1 = InferSelf<String>
let a:T1 = "aaa"
type T2 = InferSelf<number>
let b:T2 = 3
//2.推断数组(或者元组)的类型
type InferArray<T> = T extends (infer U)[] ? U : never;
type I0 = InferArray<[number, string]>; // string | number
type I1 = InferArray<string[]>; // string
type I2 = InferArray<number[]>; // number
//3.推断数组(或者元组)第一个元素的类型
type InferFirst<T extends unknown[]> = T extends [infer P, ...infer _] ? P : never
type I3 = InferFirst<[3, 2, 1]>; // 3
//4.推断函数返回值的类型
type InferReturn<T> = T extends () => infer U ? U : never
function fn(): number {
return 20
}
type T3 = InferReturn<typeof fn>
let c:T3 = 12
TS 内置了一些类型的操作符,方便我们快速的操作类型。首字母都是大写。
将所有属性都变成可选
interface User {
id: number
name?: string
status: number
super: boolean
}
type Coder = Partial<User>
// Coder 的类型变成
// type Coder = {
// id?: number | undefined;
// name?: string | undefined;
// status?: number | undefined;
// super?: boolean | undefined;
// }
const lisi: Coder = {}
注意的是,Partial 只能处理一层的数据,当有嵌套的二层数据不会进行处理。
和 Partial 相反,Required是将所有属性变成必选。
将所有属性变成只读。
type Pick<T, K extends keyof T> = { [P in K]: T[P]; }
可见需要传入两个参数。如以下例子:
interface User {
id: number
readonly name?: string
status: number
super?: boolean
}
type Coder = Pick<User, "id">
// Coder 的类型变成
// type Coder = {
// id: number;
// }
// 必传 id 属性
const lisi: Coder = {id: 1}
获取函数返回值的类型。
type getMoney = () => number
// 上面的函数返回值是 number
const money: ReturnType<getMoney> = 1000000
批量赋予某个类型。
interface User {
id: number
readonly name?: string
status: number
super?: boolean
}
type Leader = "zhangsan" | "lisi" | "wangwu"
type Coder = Record<Leader, User>
// Coder 的类型变成
// type Coder = {
// zhangsan: User;
// lisi: User;
// wangwu: User;
// }
将 某个类型 从类型集中剔除。
type ManyTypes = boolean | string | number
type newTypes = Exclude<ManyTypes, boolean>
// 剩下类型
// type newTypes = string | number
和 Exclude 相反,Extract从类型集中提取某个类型
type ManyTypes = boolean | string | number
type newTypes = Extract<ManyTypes, boolean>
// 剩下类型
// type newTypes = boolean
剔除某个属性,剩余的组建新的类型。
interface User {
id: number
readonly name?: string
status: number
super?: boolean
}
type Coder = Omit<User, "status">
// Omit 剔除 status 属性 剩余以下属性
// type Coder = {
// id: number;
// readonly name?: string | undefined;
// super?: boolean | undefined;
// }
用来过滤类型中的 null 及 undefined 类型。
type Name = string | undefined | null
type UserName = NonNullable<Name> // UserName 的类型为 string
用于获得函数的参数类型组成的元组类型。
type Param = Parameters<(name: string, age: number) => void>
// 类型 Param 为: type Param = [name: string, age: number]
tsconfig.json 是 TypeScript 项目的配置文件。
命令 tsc xxx.ts 通过配置文件编译出相应的js代码。
{
"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 // 为装饰器提供元数据的支持
}
}