TypeScript简称TS,是一门开源的编程语言。
一、TS 的特点:
- 完全兼容Javascript,是JS的超集;
- TS引入了数据类型,可以尽早定位错误,
二、TS环境搭建
Step1:全局安装typescript包
npm i typescript -g 或 yarn add global typescript
【注】查看全局包的安装路径 npm root -g
Step2:安装之后,检查是否安装成功及版本号 tsc -v
Step3:typescript初始化,tsc --init
TS初始化后,创建了ts的配置文件tsconfig.json
Step4:创建一个TS文件<hello.ts>
【注】ts文件不能直接运行,必须要转换为js文件才可以运行。
Step5:生成对应的js文件tsc hello.ts
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
eat() {
console.log(this.name + '吃饭了' + this.age + '白米饭');
}
}
let p1 = new Person('Evelyn', 22);
p1.eat();
Step6:终端使用node命令执行js文件
node hello.js
三、webpack打包TS文件
项目中可以借助webpack打包工具直接处理ts文件。
(webpack知识面匮乏,之后补充笔记)
四、TS语法
4-1 TS基础类型
//1、boolean类型
let love: boolean = true;
console.log(love ? 'hold together' : 'break up');//hold together
//2、number类型
//十进制
let num1: number = 27;
//十六进制
let num2: number = 0x6f;
//八进制
let num3 = 0o36;
//二进制
let num4 = 0b101010101;//binary
let n: number = 100;
n = '200'//报错,不能将类型“string”分配给类型“number”
//3、string类型
let str1: string = 'Evelyn-Tora-family';
console.log(str1.split('-'));//['Evelyn', 'Tora', 'family']
console.log(str1.split('-').join('^-^'));//Evelyn^-^Tora^-^family(字符串)
//4、字面量类型
// let a:100 = 200;//报错
let obj: { n: 100 } = { n: 100 };
console.log(obj);//{n: 100}
//5、any类型(很少用)
//any 类型允许变量的值为任意类型, 并且可以进行任意类型的赋值
// let x: any;
let x;
x = 100;
x = 'i love you';
console.log(x);//i love you
//如果声明的变量没有限定类型,默认为any和直接定义类型为any等同
//let x;
//6、void类型
//void 限制值为 undefined
//可以限制变量的值,更多的时候是限制函数的返回值
let v: void;
console.log(v); //undefined
//强制要求函数的返回值是undefined/没有返回值
function fn1(): void {
// return 100;//报错
return;//不会报错
//不写return语句也不会报错
}
console.log(fn1());//undefined
let fun2 = (): void =>{
return;
}
console.log(fn2());//undefined
//给函数限制返回值类型时,写法: ():类型
//【注】:void
//<a href="javascript:;"></a>
//<a href="javascript:void(0)"></a>
// <a href="javascript:函数名(实际参数)"></a>
//7、never类型(基本不用)
let a: never = true;
console.log(a);//报错,不能将类型boolean分配给类型never
function fn(): never {
throw new Error('出错啦');
}
fn();//控制台捕获错误
8、对象
//方式一:声明时限定类型为Object
//键名键值并没有规范语法进行约束
//在获取对象的键值时会报错,因为程序检测不到
let obj : Object = {
name:'Evelyn',
age:22,
sex:'女'
};
console.log(obj);//{name: 'Evelyn', age: 22, sex: '女'}
console.log(obj.name)//报错,类型Object上不存在“name”属性
//方式二
let obj1:{a:number,b:number} = {a:100,b:200};
9、数组 ts中的数组,要求[]里面所有值的数据类型统一,否则是元祖。
//方式一:
let arr1: number[] = [1, 2, 3, 4, 5, 6];
//方式二(泛型式写法)
let arr2:Array<number> = [1,2,3,4,5]
let arr3:Array<string> = ['a','b','c']
10、元祖
元祖的核心仍然为数组,只不过数组中的值的类型可以不一致。
let 变量名: [] = [];
let arr:[string,num,boolean] = ['Evelyn',27,true];
//元祖可以使用数组的API
let arr1: [Array<string>, boolean, string, number] = [['a', 'b', 'c'], true, 'a', 1];
let arr2: [string[],boolean, string, number] = [['a', 'b', 'c'], true, 'a', 1];
//两种写法等效
//二维元祖
let arr3:[boolean,number,string][] = [[true, 100, 'hello1'],[false, 200, 'hello2']]
11、枚举
限制为枚举类型的数据,更像是一种语法规范(定义常量的感jio)
enum 枚举名称{}
enum gender{
man = '男',
woman = '女'
}
let obj:Object = {
name:'Evelyn',
sex:gender.woman
}
4-2 联合类型
联合类型可以一次性声明多个类型,后期设置的变量值,在这个类型范畴中即可 每一个类型中间使用"|"分割,表示"或者"
let x: number | string | boolean;
x = true;
x = 'a';
x = 10;
4-3 类型断言
类型断言和联合类型是联合使用的,类型断言是在真正调用这些类型中的方法的时候,容易起冲突的部分需要下断言。
//语法一:(变量 as 具体哪一个数据类型)(更常用)
let x1: number | string ;
x1 = 123.456;
console.log((x1 as number).toFixed(2));
//语法二:(<具体哪一个数据类型>变量)
console.log((<number>x1).toFixed(2));
4-4 类型推断
//1. 变量声明时已赋值,推断为值对应的类型
let a = 10; //等效于 let a:number = 10
a = 'a';//报错,因为a已经被推断为number,后期赋值只能是number类型的
//2.变量声明时没有赋值, 推断为 any 类型
let a;
//3.函数的返回值推断
let fun = () => {
return 'str...';//此时函数的返回值类型已经推断为string
}
4-5 函数
//1、函数的参数的写法
let fn = (x: boolean, y: Array<number>) => {
console.log(x)
console.log(y)
}
fn(true, [1, 2, 3]);
//2、函数的返回值写法
//一般在后期开发的时候会更多情况选择自动推算函数返回值类型
let fun = (): Array<number> => {
return [1, 2, 3];
}
console.log(fun());
//3、箭头函数的完整声明
let fn: () => string = () => {
return 'hello';
}
//4、可选参数 (参数名?:数据类型)
function fun(a: number, b: string, c?: boolean) {
console.log(a)
console.log(b)
console.log(c);
}
fun(1, 'hello');
fun(1, 'hello', true);
//5、参数的默认值
//参数的默认值在函数的形参列表的末尾
//前面先定义所有必填的参数,然后在加可选的参数或者是参数的默认值
function fun(a: number, b: string, c: string = 'hello') {
console.log(a)
console.log(b)
console.log(c)
}
fun(10, 'Evelyn');
fun(10, 'Evelyn', 'world');
//6、剩余参数(ES6中的rest参数)
//rest参数:当函数调用的时候,实际参数个数不明确
//rest参数一定要在形式参数列表的结尾,并且返回的是一个纯数组
//剩余参数...args,是一个数组,所以写法:...args: number[]
function fun1(...args: number[]) {
// console.log(args);
let res = args.reduce((prev: number, item: number) => {
return prev + item;
}, 0)
console.log(res)
}
fun1(1, 2, 3);
//7、特别注意
let fun: (a: number, b: string, c?: number) => string = (x: number, y: string, z: number = 0) => {
return x + y + z;
}
console.log(fun(100, '200'));
4-6 类
4-6-1 类的定义
class Student {
//提前规定类中的属性!!!
name: string;
age: number;
//在实例化对象添加一个统一值的属性
address: string = '尚硅谷'
//构造器(构造方法/构造函数)
//构造器无需程序员手动调用,在new一个类的时候自动执行,
//每一个类只能有一个构造器
constructor(name: string, age: number) {
//在实例化对象的身上添加属性
this.name = name;
this.age = age;
}
study(): string {
return (this.name + '爱study...在' + this.address + '学习');
}
eat(food?: string): string {
return `${this.name}爱吃${food}`
}
}
let s1 = new Student('Evelyn', 22);
let s2 = new Student('Tora', 23);
console.log(s1.study());
// s2.study();
console.log(s1.eat('火锅'));
console.log(s1.eat())
4-6-2 类的继承&成员类型限制
- public(公开,默认的修饰的权限,加或者不加都可以访问) 当前类内可以直接访问,类外也可以直接访问,其被继承的子类也有权访问
- private(私有) 当前类内可以直接访问,类外不允许访问,其被继承的子类也不能访问
- protected(受保护) 当前类内可以直接访问,类外不允许访问,其被继承的子类能访问
//父类
class Father {
name: string
age: number
protected asset: string
private health: string
constructor(name: string, age: number, asset: string, health: string) {
this.name = name;
this.age = age;
this.asset = asset;
this.health = health;
}
intorduction() {
console.log(`我是${this.name},我的年龄是:${this.age}`)
}
showAsset() {
console.log(`爸爸的资产是${this.asset}`)
}
protected showHealth() {
console.log(`${this.name}的身体状况:${this.health}`)
}
}
//子类
class Son extends Father {
grade: string
//当如果子类继承了父类,如果子类没有明确表示构造器,则调用的是父类的构造器
//当所有子类的实例化对象的属性值是统一的值,则需要直接赋值,无需通过构造器
//每一个实例化对象的属性值都不相同,则必须通过构造器的形式参数传递实际参数来表示
constructor(name: string, age: number, grade: string, asset: string, life: string) {
super(name, age, asset, life);
this.grade = grade;
}
showAsset() {
console.log(`儿子的资产是${this.asset}`)
}
study() {
console.log(`${this.name}每天都得学习,现在在读${this.grade}`)
}
showHealthhh() {
console.log(this.showHealth());
}
}
let f1 = new Father('爸爸', 50, 'one billion', '延年益寿');
let s1 = new Son('好大儿', 22, '高一年级', '¥100', '茁壮成长');
//protected
// console.log(f1.asset) //报错,属性"asset"受保护,只能在类Father及其子类中访问,类外不能访问
f1.showAsset();//爸爸的资产是one billion
s1.showAsset();//儿子的资产是¥100
//private
// console.log(s1.health);//报错,属性“health”是私有属性,只能在类Father中访问
// console.log(f1.health);
// f1.showHealth();//报错,属性“showHealth”受保护,只能在类“Father”及其子类中访问
s1.showHealthhh();//好大儿的身体状况:茁壮成长(间接读取私有属性值)
//public
s1.study();//好大儿每天都得学习,现在在读高一年级
s1.intorduction();//我是好大儿,我的年龄是:22
f1.intorduction();//我是爸爸,我的年龄是:50
/*
每次new一个实例化对象时,就会调用其构造函数的constructor,
new Son('好大儿', 22, '高一年级', '¥100', '茁壮成长'),这里是实参,
constructor(name: string, age: number, asset: string, health: string),这里是形参,
如果存在继承,子类的constructor中调用super方法,将值传递给super方法,
(super方法会调用父类的构造器)
super(name, age, asset, life),这里是实参,而父类的constructor中是形参。
*/
4-7 接口
接口的作用是为了规范对象的结构(接口就是为对象服务的!)
【注】如果使用let obj:Object={a:100}的方式,由于Object中不存在属性a,所以obj.a会报错!
- 声明一个接口:
interface 接口名称{} - 在对象中使用接口:
let obj : 接口名称 = {...} - 在类中使用接口:
class Person implements 接口名称{...}
4-7-1 对象&接口
interface Person {
//属性分为可选属性、必填属性、只读属性
//1、必填属性:
//如果接口中定义的是必填属性,那么对象在调用接口类型时,必填属性要写全,否则会报错
name: string,
//2、只读属性,不允许被修改
readonly age: number,
//3、可选属性
//如果接口中定义的是可选属性,那么对象在调用接口类型时,可选属性可以不写全,不会报错
sex?: string
//必填方法
eat: () => void
//如果接口中函数返回值设置为void,不会那么严格,对象种方法有返回值也可以
//可选方法
speak?: () => string
}
let obj: Person = {
age: 22,
name: 'Evelyn',
//必填方法
eat: () => {
console.log('去吃火锅吧!')
},
//可选方法
speak: () => {
return 'Do not speak loudly'
}
}
// obj.age = 18; // 报错
console.log(obj.age)
console.log(obj.name)
console.log(obj.sex)//undefined
//方法
obj.eat();//去吃火锅吧!
//如果接口中定义的方式是可选方法时,在对象实现且调用时,
//程序认为这个方法可能存在也可能不存在
//如果存在就是接口中定义的方法值,如果不存在就是undefined
//解决方法有两个:
//1、修改tsconfig.json文件中的严格模式“strict”:false(修改了配置文件脚手架要重启)
//2、调用方法时,在调用的()前面加一个!,程序就会认为speak方法一定存在
console.log(obj.speak())//Do not speak loudly
console.log(obj.speak!())
4-7-2 类&接口
1.类中应该有什么样的属性/方法,需要使用接口来进行规范!
2.接口和类的关系是:类是实现接口中的规范(implements),每个类可以实现多个接口规范;
3.类要[全部]实现接口中规范的接口和属性
class 类名 implements 接口名称1,接口名称2...{}
- 类——类 [继承关系] class 子类 extends 父类{}
- 接口——接口 [继承关系] interface 子接口 extends 父接口{}
- 类——接口 [实现关系] class 类 implements 接口{}
interface PerInterface {
//接口中定义属性&方法的名和类型
}
//接口中可能有重复属性,所以将公共的属性单独抽离出来一个公共的接口
//所以其他的每一个接口需要来继承这个公共的接口Common
interface common {
name: string,
age: number
}
interface PerInterface1 extends common {
hobby: string
}
interface PerInterface2 extends common {
sex: string
}
class Person implements PerInterface1, PerInterface2 {
name: string;
age: number;
sex: string;
hobby: string
//类如果已经全部实现了接口规范中的属性和方法后,仍然可以给自身添加自定义属性
other: string
constructor(name: string, age: number, sex: string, hobby: string, other: string) {
this.name = name;
this.age = age;
this.sex = sex;
this.hobby = hobby;
this.other = other;
}
}
let p1 = new Person('Evelyn', 22, '女', '躺着', '接口之外的其他属性');
console.log(p1);
//Person {name: 'Evelyn', age: 22, sex: '女', hobby: '躺着', other: '接口之外的其他属性'}
4-8 泛型
泛型(generic)指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。“不确定”
- 泛型变量不固定,但是基本上都是一个字母且大写;
- 通常用的变量名是:P、I、R、U...
4-8-1 函数中的泛型变量的使用
泛型在函数中的使用,主要用于解决,在声明时不确定形参类型。
语法:
function 函数名称<泛型名称> {...}
//如果不规定返回值:function fun<P, I>(x: number, y: P, c: I): (P | I)[]
//返回值类型会报错,默认的返回值类型是一个联合类型的数组
function fun<P, I>(x: number, y: P, c: I): [P, I] {
return [y, c];
}
let res = fun(100, 'hello', 27.27);
console.log(res[0].indexOf('e'))
console.log(res[1].toFixed(1))
//创建一个函数, 实现功能: 根据指定的数count和数据value, 创建一个包含count个value的数组
function fun1<U>(count: number, value: U) {
let arr = []
for (let i = 0; i < count; i++) {
arr.push(value)
}
return arr;
}
console.log(fun1(3, 'Evelyn'));
console.log(fun1(6, 27));
4-8-2 对象中的泛型变量的使用
泛型接口——解决对象的结构不确定,主要是针对于结构做规范
//语法:
interface 接口名称<泛型变量>{
属性名1:属性值1,
属性名2:属性值2,
...
属性名n:泛型变量
}
//如果对象中的属性又是一个对象,且该对象结构不确定,需要考虑定义泛型变量
interface Common<p> {
status: number,
statusText: string,
data: p
}
interface Todolists {
id: number,
title: string,
done: boolean
}
interface Booklists {
id: number,
content: string,
author: string
}
//接口就是用定义对象的规范
let obj1: Common<Todolists> = {
status: 200,
statusText: 'ok',
data: {
id: 1,
title: '任务一',
done: false
}
}
let obj2: Common<Booklists> = {
status: 200,
statusText: 'ok',
data: {
id: 1,
content: '西游记',
author: '吴承恩'
}
}
console.log(obj1.status)
console.log(obj1.data.title)
console.log(obj2.data.author)
4-8-3 接口中的泛型变量的使用
【注】泛型引用,要写在接口名、函数名、类名的后面
当对象中的属性点不出来的时候,要给对象加一个接口,进行对象规范
class MyClass<P>{
num: number
str: string
arr: P[]
constructor(num: number, str: string, arr: P[]) {
this.num = num;
this.str = str;
this.arr = arr;
}
}
interface MyInt {
id: number,
title: string,
done: false
}
//数组中是number类型
let mc1 = new MyClass<number>(1, 'aa', [1, 2, 3]);
console.log(mc1)//MyClass {num: 1, str: 'aa', arr: Array(3)}
//数组中是对象类型
let mc2 = new MyClass<MyInt>(2, 'bb', [{ id: 1, title: '任务一', done: false }, { id: 1, title: '任务一', done: false }]);
console.log(mc2.arr[0].done)
//如果不指明泛型变量的数据类型,mc2.arr. 就点不出来对象的属性名
4-9 其他
4-9-1 类型声明
typeof:是用来获取 变量的数据类型(number、string、undefined、null、boolean、function) keyof :是用来获取 接口/类的属性组成的联合类型("属性名1"|"属性名2"|"属性名3")
- type关键字声明类型
type 类型名称 = 类型值- 类型只能用来声明后使用,不能打印输出!
//typeof使用
let str1 = 'aa';
type strings = typeof str1;
let str: strings = 'Evelyn'
console.log(str)
//type声明类型多用于以下场景:
interface Booklist {
id: number;
bookName: string;
author: string
}
//定义一个books类型的变量
//是一个数组,数组中的每一个元素是一个对象(Booklist)
type books = Booklist[];
let arr1: books = [{ id: 1, bookName: '西游记', author: '吴承恩' }];
console.log(arr1[0].author)
//keyof使用
type book = keyof Booklist;
// console.log(book);//报错,类型只能用来声明后使用,不能打印输出!!!!
function funnn(x: book) {
console.log(x)
}
funnn('id');//id
funnn('bookName')//bookName
4-9-2 TS脚手架
let root = document.querySelector('#root') as HTMLElement;
//要做类型断言