TypeScript笔记

TypeScript开发环境搭建

  1. 安装node.js
  2. 全局安装npm i -g typescript
  3. 创建一个ts文件
  4. 使用tsc对ts文件进行编译tsc xxx.ts

基本类型

类型声明

语法:

let 变量: 类型;

let 变量: 类型 = 值;

function fn(参数: 类型, 参数: 类型): 类型 {
    ...
}

TS中的类型

类型例子描述
number1, -33, 2.5任意数字
string‘hi’, “hi”, hi任意字符串
booleantrue、false布尔值true或false
字面量其本身限制变量的值就是该字面量的值
any*任意类型
unknown*类型安全的any
void空值(undefined)没有值(或undefined)
never没有值不能是任何值
object{name:‘孙悟空’}任意的JS对象
array[1,2,3]任意JS数组
tuple[4,5]元素,TS新增类型,固定长度数组
enumenum{A, B}枚举,TS中新增类型

number

let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;

boolean

let isDone: boolean = false;

string

let color: string = "blue";
color = 'red';

let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}.

I'll be ${age + 1} years old next month.`;

字面量

通过字面量可以确定变量的取值范围

// color只可以取'red','blue','black'其中一个值
let color: 'red' | 'blue' | 'black';
// num只可以取1,2,3,4,5其中一个值
let num: 1 | 2 | 3 | 4 | 5;

any(任意类型)

相当于该变量关闭了TS的类型检测

let d: any = 4;
d = 'hello';
d = true;

unknown(类型安全的any)

let abc:unknown = true;
let s:string = 'hellow';
// unknow类型的变量不能直接赋值给其他变量
if(typeof s == 'string') { // 只有s为string时才能够赋值
    abc = s;
}

void(空值或undefined)

在函数中,表示函数没有返回值

function fun():void {
    return
}

never(永远不会有值)

比如报错的函数,因为它永远不会有返回值

function error(message: string): never {
  throw new Error(message);
}

object

let a: object;
a = {};

// 语法:{属性名:属性值,属性名:属性值}
// 属性名后面加一个问好,表示这个属性可选
let b:{name: string, age?: number};
b = {name: 'aaa'};

let c:{name: string, [aaa: string]: any};
c = {name: 'adf', age: 18, show: true};

// 声明d为一个对象,有两个参数类型都是number,并且返回值也是number类型
let d: (a: number, b: number)=>number;

array

语法:

  1. 类型[];
  2. Array<类型>;
// 表示字符串类型数组
let a: string[];
a = ['1', '2', '3'];

// 表示数值类型数组
let b: number[];
b = [1, 2, 3];

// 表示数值类型数组
let c:Array<number>;
c = [1, 2, 3];

tuple(元组)

元组是固定长度的数组

// 声明一个长度为4的数组,并且规定了数据类型
let a: [string, string, boolean, number];

enum(枚举)

// 枚举(把可能的情况列举出来)
enum Gender {
    nan = 0,
    nv = 1
}
let i:{name: string, gender: Gender};
i = {
    name: '刘德华',
    gender: Gender.nan
}

类型或运算

// 声明的j既可以是string类型,又可以是number类型
let j: string | number;

类型别名

可以简化类型的使用

type mytype = 1 | 2 | 3 | 4 | 5;
let a: mytype;
a = 1;
a = 6; // 报错

类型断言

告诉解析器变量的实际类型

语法:

  1. 变量 as 类型
  2. <类型>变量
 a = b as string;
 a = <string>b;

接口

TypeScript 的核心原则之一是对值所具有的结构进行类型检查。我们使用接口(Interfaces)来定义对象的类型。接口是对象的状态属性和行为方法的描述

语法:

interface xxx {}

可选属性

可选属性名字定义的后面加一个 ? 符号

只读属性

在属性名前用 readonly 来指定只读属性

例:定义人这个接口,id是只读的,sex是可选的

interface People {
	readonly id: number,
	name: string,
	age: number,
	sex?: string
}

函数类型

通过接口的方式作为函数的类型来使用

interface Search {
	// 定义一个调用签名
	(source: string, subString: string): boolean
}

// 查找字符串函数
const searchString: Search = function(source: string, subString: string): boolean {
	return source.search(subString) > -1;
}

searchString('你好','你'); // true
searchString('你好','w'); // false

类类型

TypeScript能够用接口来明确的强制一个类去符合某种契约。

  1. 类可以实现一个接口

    // 定义接口
    interface Alarm {
      alert(): any;
    }
    // 实现接口
    class Car implements Alarm {
      alert() {
          console.log('Car alert');
      }
    }
    // 创建对象
    const car = new Car();
    
  2. 还可以实现多个接口

    // 定义接口
    interface Alarm1 {
    	alert(): any;
    }
    // 定义接口
    interface Warn {
    	warn(): any;
    }
    // 实现接口
    class Car implements Alarm,Warn {
        alert() {
        	console.log('Car alert');
        }
        warn() {
        	console.log('Car warn');
        }
    }
    // 创建对象
    const car = new Car();
    
  3. 接口继承

interface error extends Alarm, Light {};

接口和接口之间叫继承,用extends关键字,类和接口之间叫实现,用implements关键字

TS中类的定义及使用

class Person {
    name: string
    age: number
    sex: string
    // 构造函数
    constructor(name: string, age: number, sex:string = "男") {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    // 实例化方法
    hello(str: string) {
        console.log(`我叫${this.name},${str}`);
    }
}

const person = new Person('张三', 18, '男');
person.hello('大家好呀')

类的继承

A类继承了B类,A类叫子类,B类叫基类

子类---->派生类

基类---->超类

一旦发生了继承的关系,就出现了父子类的关系

// 定义B类(父类,超类)
class BClass {
    name: string
    age: number

    constructor(name:string , age:number) {
        this.name = name;
        this.age = age;
    }
    hello(str:string) {
        console.log(`我叫${this.name},${str}`);
    }
}

// 定义A类(子类)
class AClass extends BClass {
    constructor(name:string , age:number) {
        // 调用父类中的构造函数,使用的super
        super(name, age);
    }
    hello(){
        super.hello('我是B类的子类A类')
    }
}

const B = new BClass('张三', 18);
B.hello('bbbbb');
const A = new AClass('李四', 18);
A.hello();

类和类之间有继承关系,需要使用extends关键字

子类中调用弗雷德构造函数,使用super()关键字(包括调用方法),子类中可以重写父类的方法

多态

父类型的引用指向子类型的对象,不同类型的对象针对相同的方法,产生了不同的行为

// 定义B类(父类,超类)
class BClass {
    name: string
    age: number

    constructor(name:string) {
        this.name = name;
    }
    hello(str:string) {
        console.log(`我叫${this.name},${str}`);
    }
}

// 定义A类(子类)
class AClass extends BClass {
    constructor(name:string) {
        // 调用父类中的构造函数,使用的super
        super(name);
    }
    hello(str:string = '我是B类的子类A类'){
        super.hello(str)
    }
}

// 定义C类(子类)
class CClass extends BClass {
    constructor(name:string) {
        // 调用父类中的构造函数,使用的super
        super(name);
    }
    hello(str:string = '我是B类的子类C类'){
        super.hello(str)
    }
}

const B = new BClass('张三');
B.hello('bbbbb');
const A = new AClass('李四');
A.hello('aaaaa');
const C = new CClass('王五');
C.hello('ccccc');

// 用父类类型创建子类的对象
const AA = new AClass('李四1号');
AA.hello('aaaaa'); // 我叫李四1号,aaaaa
const CC = new CClass('王五1号');
CC.hello('ccccc'); // 我叫王五1号,ccccc

function helloWord(B: BClass) {
    B.hello('函数中调用B.hello');
}

helloWord(AA); // 我叫李四1号,函数中调用B.hello
helloWord(CC); // 我叫王五1号,函数中调用B.hello

公共、私有、受保护的修饰符

修饰符:描述类中的成员(属性,构造函数,方法)的可访问性

默认为 public

代表公共的,任何位置都可以访问类中的成员

class Person {
    name: string
    constructor(name:string) {
        this.name = name;
    }
    hello() {
        console.log(`我的名字叫${this.name}`);
    }
}

const person = new Person('张三');
console.log(person.name); // 张三
person.hello(); // 我的名字叫张三

private

代表私有的,类中的成员使用private修饰,外部是无法访问这个成员数据的,子类中也无法访问成员数据的

class Person {
    private name: string
    constructor(name:string) {
        this.name = name;
    }
    hello() {
        console.log(`我的名字叫${this.name}`);
    }
}

const person = new Person('张三');
console.log(person.name); // 属性“name”为私有属性,只能在类“Person”中访问
person.hello(); 

protected

代表受保护的,在外部是无法访问的,在派生类中仍然可以访问

readonly

对类中的属性成员进行修饰,修饰后,该属性成员,就不能在外部被随意的修改了

  • 构造函数中,可以对只读的成员进行修改
  • 类中的方法也不能修改readonly修饰的成员属性值
class Person {
    readonly name: string
    constructor(name:string) {
        this.name = name;
    }
    hello() {
        console.log(`我的名字叫${this.name}`);
        this.name = '不告诉你'; // 报错:类中的方法也不能修改readonly修饰的成员属性值
    }
}

const person = new Person('张三');
console.log(person);
console.log(person.name);
person.name = '不告诉你'; // name是只读属性,不可以被修改
  • 构造函数中的参数一旦使用了readonly修饰后,该参数可以叫参数属性,并且在类中就有了一个该参数的属性成员
  • 构造函数中的参数也可以使用public、private、protected来修饰参数
class Person {
    // 构造函数中的参数一旦使用了readonly修饰后,该参数可以叫参数属性,并且在类中就有了一个该参数的属性成员
    constructor(readonly name:string) {

    }
}

const person = new Person('张三');
console.log(person);
console.log(person.name);

存取器

让我们可以有效控制对象中成员的访问,通过getters和setters来进行操作

class FullName {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    // 读取器
    get fullName() {
        return this.firstName + '-' + this.lastName;
    }
    // 设置器
    set fullName(val) {
        this.firstName = val.substr(0, 1);
        this.lastName = val.substr(1, 1);
    }
}
const myName = new FullName('张', '三');
console.log(myName.fullName); // 张-三
myName.fullName = '李四';
console.log(myName.fullName); // 李-四

静态属性

在类中通过static修饰的属性或者方法,那么就是静态的属性及静态的方法,也成静态成员

  • 不能通过实例对象直接调用静态属性来使用
class Person {
    static str: string = 'hello'
    constructor(str:string) {
    }
    static hello() {
        console.log('aaaaa')
    }
}
Person.str = 'hello';
// 通过类名.静态属性的方式来访问该成员数据
console.log(Person.str); // hello
// 通过类名.静态属性的方式来设置该成员数据
Person.str = '你好';
console.log(Person.str); // 你好
// 通过类名.静态方法的方式来调用内部的静态方法
Person.hello(); // aaaaa

抽象类

  • 包含抽象方法,抽象方法不能有任何的具体内容的实现,也可以包含实例方法
  • 抽象类是不能被实例化,是为了让子类进行实例化及实现内部的抽象方法
  • abstract 关键字是用于定义抽象类和在抽象类内部定义抽象方法
abstract class Animal {
    abstract name:string;
    abstract eat()
    run() {
        console.log('我在跑')
    }
}

class Dog extends Animal {
    // 实现抽象类中的方法
    name: string = '大黄';
    eat() {
        console.log('狗狗爱吃骨头');
    }
}

class Cat extends Animal {
    name: string = "汤姆";
    eat() {
        console.log('猫抓老鼠吃');
    }
}

const dog = new Dog();
dog.eat(); // 狗狗爱吃骨头
dog.run(); // 我在跑
console.log(dog.name); // 大黄

const cat = new Cat();
cat.eat(); // 猫抓老鼠吃
cat.run(); // 我在跑
console.log(cat.name); // 汤姆

函数

封装了一些重复使用的代码,需要的时候之间调用

函数类型

为函数定义类型

// 参数x和y为number类型,返回值为number类型
function add(x: number, y: number): number {
    return x + y
}

let myAdd = function(x: number, y: number): number { 
    return x + y
}

书写完整函数类型

let myAdd2: (x: number, y: number) => number = 
function(x: number, y: number): number {
    return x + y
}

可选、默认和剩余参数

  1. 可选参数:函数在声明时,内部的参数用?进行修饰,那么该参数可以传入也可以不传
  2. 默认参数:函数在声明时,内部的参数有自己的默认值
  3. 剩余参数:放在函数声明的时候所有的参数的最后
// 可选参数,默认参数
function buildName(firstName: string='诸葛', lastName?: string): string {
    if (lastName) {
      return firstName + '-' + lastName
    } else {
      return firstName
    }
}
console.log(buildName('诸葛', '孔明')); // 诸葛-孔明
console.log(buildName('诸葛')); // 诸葛
console.log(buildName()); // 诸葛

// 剩余参数
function info(x: string, ...args: string[]) {
    console.log(x, args);
}
info('abc', 'c', 'b', 'a');  // abc ["c", "b", "a"]

函数重载

函数重载: 函数名相同, 而形参不同的多个函数

重载函数声明:当调用函数传入参数类型错误时报错

// 重载函数声明
function add (x: string, y: string): string
function add (x: number, y: number): number

// 定义函数实现
function add(x: string | number, y: string | number): string | number {
  if (typeof x === 'string' && typeof y === 'string') {
    return x + y
  } else if (typeof x === 'number' && typeof y === 'number') {
    return x + y
  }
}

console.log(add(1, 2)); // 3
console.log(add('a', 'b')); // ab

泛型

在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性

泛型使用场景

创建一个函数, 实现功能: 根据指定的数量 count 和数据 value , 创建一个包含 countvalue 的数组

function createArray2 <T> (value: T, count: number) {
    const arr: Array<T> = []
    for (let index = 0; index < count; index++) {
        arr.push(value)
    }
    return arr
  }
const arr3 = createArray2<number>(11.156165, 3)
console.log(arr3); // [11.156165, 11.156165, 11.156165]
console.log(arr3[0].toFixed(2)); // 11.16
const arr4 = createArray2<string>('addda', 3)
console.log(arr4); // ["addda", "addda", "addda"]
console.log(arr4[0].split('')); // ["a", "d", "d", "d", "a"]

多个泛型参数的函数

function swap <K, V> (a: K, b: V): [K, V] {
    return [a, b]
}
const result = swap<string, number>('a3dbc', 123)
console.log(result[0].length, result[1].toFixed()); // 5 "123"

泛型接口

  • 在定义接口时, 为接口中的属性或方法定义泛型类型
  • 在使用接口时, 再指定具体的泛型类型

需求:定义一个类,用来存储用户的相关信息(id,名字,年龄),通过一个类的实例对象调用add的方法可以添加多个用户信息对象,调用getById方法可以通过id查找用户

interface IbaseCRUD <T> {
  data: T[]
  add: (t: T) => void
  getById: (id: number) => T
}

class User {
  id?: number; //id主键自增
  name: string; //姓名
  age: number; //年龄

  constructor (name, age) {
    this.name = name
    this.age = age
  }
}

class UserCRUD implements IbaseCRUD <User> {
  data: User[] = []
  
  add(user: User): void {
    user = {...user, id: Date.now()}
    this.data.push(user)
    console.log('保存user', user.name)
  }

  getById(id: number): User {
    return this.data.find(item => item.id===id)
  }
}


const userCRUD = new UserCRUD()
userCRUD.add(new User('tom', 12)) // 保存user tom
userCRUD.add(new User('tom2', 13)) // 保存user tom2
console.log(userCRUD.data) // [ {name: "tom", age: 12, id: 1609853726232}, {name: "tom2", age: 13, id: 1609853726232}]

泛型类

在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型

class GenericNumber<T> {
  zeroValue: T
  add: (x: T, y: T) => T
}

let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = function(x, y) {
  return x + y 
}

let myGenericString = new GenericNumber<string>()
myGenericString.zeroValue = 'abc'
myGenericString.add = function(x, y) { 
  return x + y
}

console.log(myGenericString.add(myGenericString.zeroValue, 'test')); // abctest
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12)); // 12

泛型约束

interface Lengthwise {
  length: number;
}

// 指定泛型约束
function fn2 <T extends Lengthwise>(x: T): void {
  console.log(x.length)
}

console.log(fn2('dsfjkalkdsfj')); // 12
// console.log(fn2(13213)); 报错,因为number类中没有length属性

声明文件

  • 当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。
  • 声明语句: 如果需要ts对新的语法进行检查, 需要要加载了对应的类型说明代码
  • 下载声明文件: npm install @types/jquery --save-dev
import jQuery from 'jquery'
jQuery('选择器')

内置对象

ECMAScript 的内置对象

let b: Boolean = new Boolean(1)
let n: Number = new Number(true)
let s: String = new String('abc')
let d: Date = new Date()
let r: RegExp = /^1/
let e: Error = new Error('error message')

BOM 和 DOM 的内置对象

const div: HTMLElement = document.getElementById('test')
const divs: NodeList = document.querySelectorAll('div')
document.addEventListener('click', (event: MouseEvent) => {
  console.dir(event.target)
})
const fragment: DocumentFragment = document.createDocumentFragment()

TS编译选项

自动编译文件

tsc xxx.ts -w

自动编译整个项目

  1. 先在项目根目录下创建一个ts的配置文件tsconfig.json
  2. tsconfig.json是一个JSON文件,添加配置tsconfig.json文件
  3. 使用tsc指令,则可以自动将当前项目下的所有ts文件编译为js文件
  4. tsconfig.json可以使用注释

配置选项

一个*表示任意文件,两个*表示任意目录

include:指定那些ts文件需要被编译

  • 默认值:["**/*"]
{
	"include":[
	// src文件下任意目录中的任意文件都会被编译
	"src/**/*", "tests/**/*"
	]
}

exclude:指定那些ts文件不需要被编译

  • 默认值:[“node_modules”, “bower_components”, “jspm_packages”]

compilerOptions

  • 编译选项是配置文件中非常重要也比较复杂的配置选项
  • 在compilerOptions中包含多个子选项,用来完成对编译的配置
  • 例:
"compilerOptions": {
    "target": "ES6",
    "lib": ["ES6", "DOM"],
    "outDir": "dist",
    "outFile": "dist/aa.js",
    "allowJs": true,
    "checkJs": true,
    "removeComments": true,
}
target:设置ts代码编译的目标版本
  • 可选值:ES3(默认)、ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext
lib:指定代码运行时所包含的库
  • 可选值:ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext、DOM、WebWorker、ScriptHost …
module:设置编译后代码使用的模块化系统
  • 可选值:CommonJS、UMD、AMD、System、ES2020、ESNext、None
outDir:编译后文件的所在目录
outFile:将所有的文件合并编译为一个js文件(慎用,和module容易冲突)
allowJs:是否对js文件编译
  • 默认值:false
checkJs:是否检查js代码
  • 默认值:false
removeComments:是否移除注释
  • 默认值:false
noEmit:不生成编译后的文件
  • 默认值:false
noEmitOnError:当有错误时不生成编译文件
  • 默认值:false
strict:启用所有的严格检查
  • 默认值:true
alwaysStrict:严格模式对代码进行编译
  • 默认值:false
noImplicitAny:禁止隐式的any类型
  • 默认值:false
noImplicitThis:禁止类型不明确的this
  • 默认值:false
strictNullChecks:严格的空值检查
  • 默认值:false

extends:定义被继承的配置文件(不常用)

files:指定被编译文件的列表(不常用)

只有需要编译的文件少时才会用到

"files": [
    "core.ts",
    "sys.ts",
    "tsc.ts"
]

使用webpack打包TS

下载依赖

npm add -D typescript
npm add -D webpack webpack-cli
npm add -D webpack-dev-server
npm add -D html-webpack-plugin clean-webpack-plugin
npm add -D ts-loader
npm add -D cross-env

配置打包命令

"dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js",
"build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"

运行与打包

npm dev
npm build
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值