TypeScript学习笔记
数据类型
TypeScript包含的数据类型:
boolean
布尔类型number
数值类型string
字符串类型array
数组类型void
空类型undefined
any
任意类型null
空类型NaN
非数值enum
枚举类型tuple
元组类型unknown
不可预先定义的类型never
永远不知道什么类型
基本写法
let a: boolean = false;
let b: number = 1;
let c: string = 'c';
let g: any = 1;
g = '1212';
g = false;
g = undefined;
g = null;
//数组
let arr1: Array<string> = ['','']
let arr2: Array<string|number> = ['a',1,'A']
let arr3: number[] = [1,2]
let arr4: (string|number)[] = [2,'B','b']
//元组类型
let f: [string,number] = ['hello', 5];
//枚举类型
enum Gender{
BOY,
GIRL
}
let g1: Gender = Gender.BOY;
let g2: Gender = Gender.GIRL;
console.log(Gender); //{0: 'BOY', 1: 'GIRL', BOY: 0, GIRL: 1}
console.log(g1); //0
console.log(g2); //1
const enum Color {
RED=1,
YELLOW,
BLUE,
}
console.log(Color.RED,Color.YELLOW,Color.BLUE); //1 2 3
联合类型
let x: number|string|boolean|null|undefined;
x = 1;
x = null;
x = undefined;
void类型
- void类型表示没有任何类型
function A(): void {
return undefined
}
never类型
- never 是指没法正常结束返回的类型,一个必定会报错或者死循环的函数会返回这样的类型。
//一个函数永远不会返回,那么他的返回值类型就是never
function fun(): never{
while(true){
console.log('哈哈')
}
}
//如果函数一定要抛出错误,那么他也永远不会正常结束,他的返回类型也是never
function fun():never{
throw Error('错误');
}
类型断言
- 强行告诉ts我是什么类型。类型断言好比其它语言里的类型转换,但是它不进行特殊的数据检查和解构。
// 第一种写法:尖括号<>写法
let h: any = 'abc'
let h_len: number = (<string>h).length;
console.log(h_len) // 3
// 第二种写法:as写法
let h: string|number|boolean;
h = 'git'
console.log((h as string).length)
在TypeScript里使用JSX时,只有 as语法断言是被允许的。
函数
函数定义
function fun2 (str: string): string {
return `${str},你好`
}
fun2('JIKA')
函数表达式
// 定义一个类型来约束函数表达式
type FunRestrain = (x: string,y: string) => string; //此处不是箭头函数,只是用来分割
// 必须传入类型约束的参数类型、参数个数,返回参数类型,否则报错
let createName: FunRestrain = (firstName: string, lastName: string): string =>{
return firstName+lastName;
}
createName('张','三')
函数重载
let obj: any = {};
function attr (val: string): void;
function attr (val: number): void;
//function attr(val: string | number) :void; 这种写法和上面两行的效果相同
function attr (val: any): void{
if(typeof val == 'string'){
obj.name = val
} else if (typeof val == 'number'){
obj.age = val
}
}
attr('linjun')
attr(10)
// attr(true) //报错,没有与此调用匹配的重载。
函数的其他补充
可选参数 ?
function mFun (x?: string) {
console.log(x)
}
mFun('Q') //Q
mFun() //undefined
默认参数
function mFun (x: string = 'K') {
console.log(x)
}
mFun('Q') // Q
mFun() // K
- 注意:不能同时使用 可选参数和默认参数
剩余参数
function fun1(...params: number[]) {
console.log(params)
}
fun1(1,2,3,5) // [1, 2, 3, 5]
function fun2(...params: Array<string|number>) {
console.log(params)
}
fun2(1,2,3,'AA',5) // [1, 2, 'AA', 5]
//若要传入其他参数,则剩余参数必须放在最后
function sum(str: string, ...params: (number|string)[]) {
console.log(params)
params.push(str)
console.log(params)
}
sum('看看',1,2,3,'AA',5) //[1, 2, 3, 'AA', 5]
//[1, 2, 3, 'AA', 5, '看看']
类
基本应用
class Cat {
color: string;
constructor (color: string) {
this.color = color
}
getColor (): void {
console.log(this.color)
}
}
let a_cat = new Cat('orange')
console.log(a_cat)
运行结果:
class Animal {
color: string;
size: string;
constructor (color: string, size?: string) {
this.color = color
this.size = size
}
get colour () {
return this.color
}
set colour (param: string) {
this.color = param
}
get shape () {
return this.size
}
set shape (param: string) {
this.size = param
}
}
let cat = new Animal('orange')
console.log(cat.color, cat.size) // orange undefined
cat.color = 'white'
cat.size = 'large'
console.log(cat.color, cat.size) // white large
console.log(cat.shape()) //报错。get和set访问器不能被外部调用
类的继承 extends
- 继承可以描述类与类之间的关系。比如:A和B都是类,B是A,那么A和B形成继承关系,我们把A叫做父类,B叫做子类。
类的重写
子类可以重写父类的属性和方法,但是不能改变父类其类型,类型必须匹配。
- 子类重写父类的属性
class People {
eyes: number = 2;
body: boolean = true;
}
class Boy extends People{
eyes: number = 1; // Boy类从People类继承的eyes属性的值被改变
// body: string = '存在'; // 报错:不能将类型“string”分配给类型“boolean”。
hand: string = '2只手'; // Boy类的自有属性
}
let kid = new Boy();
console.log(kid.eyes) // 1
console.log(kid.hand) // 2只手
- 子类重写父类的方法
classPeople {
getFeature () {
console.log('我是人类')
}
otherFeature () {
console.log('我需要学习')
}
}
class Boy extends People{
getFeature () {
console.log('我是个小朋友')
}
}
let kid = new Boy();
kid.getFeature() // 我是个小朋友
kid.otherFeature() // 我需要学习
this关键字
- 在继承关系中,this的指向是动态的,根据调用者来确定this指向。
class Person {
name: string = '一个人';
sayHello () {
console.log(`你好,我是${ this.name }`)
}
}
class Man extends Person {
name: string = '罗森';
sayHello () {
console.log(`你好,我是${ this.name }`)
}
}
let m = new Man()
m.sayHello() //这里是m调用的,它指向Man这个类,所以输出 “你好,我是罗森”
super关键字
- 当我们在子类中调用父类的方法时,可以使用super关键字
class Person {
name: string = '';
sayHello () {
console.log(`你好,我叫${ this.name }。`)
}
}
class Student extends Person {
name: string = '豆豆';
exp () {
super.sayHello() //你好,我叫豆豆。
this.sayHello() //你好,我叫豆豆。
}
}
let h = new Student()
h.exp()
在子类没有重写父类方法的前提下,使用super调用父类的方法时,super和this的效果相同。
但是,如果子类重写了父类的方法,则super和this的效果会有差异。
class Person {
name: string = '';
sayHello () {
console.log(`你好,我叫${ this.name }。`)
}
}
class Student extends Person {
name: string = '豆豆';
// 重写父类的方法
sayHello () {
console.log(`${ this.name },很高兴遇见你!`)
}
exp () {
super.sayHello() //你好,我叫豆豆。
this.sayHello() //豆豆,很高兴遇见你!
}
}
let h = new Student()
h.exp()
抽象类 abstract
- JS中没有抽象类,它是TS中提出来的。有时候,某个类只表示抽象的概念,主要用于提取子类共有的成员,而不是直接创建它的实例,这时该类可以作为抽象类。只要给类前面加上abstract,就可以表示抽象类,
抽象类不可以创建实例。
- 抽象类和其他类区别不大,只是不能被实例化。
- 抽象类是专门用来被继承的类,抽象类中可以添加抽象方法,抽象方法以abstract开头,没有方法体。
- 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写。
- 接口里的方法都是抽象的。
抽象父类
abstract class People {}
class man extends People {}
class woman extends People {}
// let P = new People() //报错:无法创建抽象类的实例。
let m = new man()
let w = new woman()
抽象成员
- 在父类中,可能只知道某些成员是必须存在的,就像人都有名字,但在父类中没办法直接书写具体的名字,只能在子类中知道,因此,需要一种强约束,让继承父类的子类必须实现该成员。
注意:只有在抽象类中才能使用抽象成员
abstract class Person { //抽象类
abstract readonly name: string //抽象成员
}
//子类实现抽象成员,第一种方式
class boy extends Person {
readonly name: string = '凯凯'
}
//子类实现抽象成员,第二种方式
class girl extends Person {
readonly name: string;
constructor () {
super() //派生类的构造函数必须包含 "super" 调用。访问派生类的构造函数中的 "this" 前,必须调用 "super"。
this.name = '娜丽'
}
}
//子类实现抽象成员,第三种方式
//这里没有写 set 访问器,所以本身就是只读的,所以可以不用 readonly,也没法用
class baby extends Person {
get name (): string {
return '小夏'
}
}
const b = new boy()
const g = new girl()
const bb = new baby()
console.log(b.name, g.name, bb.name) //凯凯 娜丽 小夏
静态成员 static
- 静态成员就是附着在类上的成员,包括属性和方法。如果在JS当中,我们可以说附着在构造函数上的成员。使用static修饰的成员称作静态成员,静态成员也称作非实例成员,它是属于某个类的,而实例成员也叫对象成员,它是属于某个类的对象。
- 当类中的方法被声明为static时,其 实例化对象不可调用该方法,只有 类 本身 ,以及 子类 可以调用。
- 静态方法中的this指向当前类,而实例方法中的this指向当前对象
class Person {
name_1: string = '哈哈';
static gender: string = '女';
static sayHello () {
// console.log(this.name_1) // undefined 类型“typeof Person”上不存在属性“name_1”。
console.log(this.gender) // 女 在静态方法中,取到当前所在类的静态变量
this.gender = '男';
console.log(this.gender) // 男
console.log(this.name) // Person 该class的名称
console.log(this) //Person类 class Person { ... }
}
}
Person.sayHello()
let p = new Person()
console.log(p.name_1) //哈哈
// p.sayHello() //Uncaught TypeError: p.sayHello is not a function
继承类的情况:
class Person {
name_1: string = '哈哈';
static gender: string = '女';
static sayHello () {
// console.log(this.name_1) // undefined 类型“typeof Person”上不存在属性“name_1”。
console.log(this.gender) // 女 在静态方法中,取到当前所在类的静态变量
this.gender = '男';
console.log(this.gender) // 男
console.log(this.name) // Men 该class的名称
console.log(this) //Men类 class Men extends Person { ... }
}
}
interface sleep { //接口里的方法都是抽象的
time():void;
}
class Men extends Person {
sayHello () {
console.log('我是Men的方法')
}
}
let m = new Men()
console.log(m.name_1) //哈哈
m.sayHello() //我是Men的方法
Men.sayHello()
Men.sayHello() 调用的是Men内自定义的sayHello()方法,和Person中的sayHello()方法相互独立,互不影响。
其他修饰符
通过类型的继承来讲一下访问修饰符 public protected private
public
- 修饰属性和方法,里里外外都能访问,自己、自己的子类、其他类都能访问
- 不写修饰符,默认就是 public
protected
- 受保护的,类内部以及其子类内部非 static 方法内可访问,实例化对象和子类也不可访问
class Person {
name: string = '';
age: number = 0;
protected eat () {
console.log('吃饭')
};
eat2 () {
this.eat()
console.log('吃水果')
}
}
let p = new Person()
p.eat2() //吃饭 吃水果
// p.eat() //属性“eat”受保护,只能在类“Person”及其子类中访问。
// Person.eat() //Uncaught (in promise) TypeError: Person.eat is not a function
class Person {
name: string = '';
age: number = 0;
protected eat () {
console.log('吃饭')
};
}
class Boy extends Person {
boy1 () {
this.eat()
}
private boy2 () {
this.eat()
}
protected boy3 () {
this.eat()
}
static boy4 () {
// this.eat() //类型“typeof Boy”上不存在属性“eat”。
}
}
let b = new Boy()
b.boy1() // 吃饭
// b.boy2() //属性“boy2”为私有属性,只能在类“Boy”中访问。
// b.boy3() //属性“boy3”受保护,只能在类“Boy”及其子类中访问。
// b.eat() //属性“eat”受保护,只能在类“Person”及其子类中访问。
// Boy.eat() //类型“typeof Boy”上不存在属性“eat”。
private
- 私有的,只能在本类内部使用,实例化对象和子类都不能访问
class Person {
name: string = '';
age: number = 0;
private eat () {
console.log('吃饭')
};
eat2 () {
this.eat()
console.log('吃水果')
}
}
let p = new Person()
p.eat2() // 吃饭 吃水果
// p.eat() //属性“eat”为私有属性,只能在类“Person”中访问。
// Person.eat() //Uncaught (in promise) TypeError: Person.eat is not a function
接口
用来表示对象的形状
interface Student {
name:string,
study():void,
eat?():void,
}
let s:Student = {
name: 'Star',
study () {
console.log('十点睡觉')
},
// sleep () {} //报错 “sleep”不在类型“Student”中。
}
用来描述行为的抽象
interface People {
name:string,
study():void,
eat():void,
}
interface Man extends People {
sleep(): string,
}
//implements 实现,一个新的类,从父类或者接口实现所有的属性和方法,同时可以重写属性和方法,包含一些新的功能
class Kid implements Man {
name:string;
study() {};
eat() {};
sleep() {
return '睡觉'
};
age: number; //Kid类自定义属性
jump () { //Kid类自己的方法
console.log('跳远')
}
}
//extends 继承,一个新的接口或者类,从父类或者接口继承所有的属性和方法,不可以重写属性,但可以重写方法,可以包含一些新的功能
class Boy extends Kid {
// age: string; //报错 不可以重写Kid类中的属性
jump () { //可以重写Kid类中的方法
console.log('重写Kid类中的jump')
};
drink () {
console.log('自定义方法')
}
}
补充
- 接口 不能实现 接口或者类,所以实现只能用于类身上,即类可以实现接口或类
- 接口 可以继承 接口或者类
- 类不可以继承接口,类只能继承类
- 可多继承或者多实现
接口类型的 必要属性 和 可选属性
interface Animal {
readonly id: number, //必要属性
name: string, //必要属性
[otherProp: string]: any //任意属性
}
let cat:Animal = {
id: 1,
name: 'Kitty',
age: 2,
sex: 'female'
}
用接口规范、定义函数
interface SalePricefun {
(price:number): number
}
let goods:SalePricefun = (price:number):number => {
return price*1.08
}
interface WeekFun {
(): number
}
let order:WeekFun = () => {
return Math.round(Math.random()*10)
}
// 接口定义函数的入参类型
interface RulesFun {
(...args: any[]):number
}
let other:RulesFun = (...params:number[]):number => {
console.log(params)
return params.reduce((pre,cur) => pre+cur)
}
other(3,'1',2) //调用函数时,在RulesFun接口定义的入参为any类型的条件下,不受other函数规定的入参类型限制
可索引接口,对数组或对象进行约束
// 索引为number类型
interface Arr {
[index: number]: string
}
let n:Arr = ['w','e']
console.log(n) //['w', 'e']
// 索引为string类型
interface Obj {
[index: string]: string
}
let o = {
'a': 1,
'b': 2
}
console.log(o) //{a: 1, b: 2}
类的接口,用接口约束构造函数的类型
class Animal{
constructor(public name:string, public age: number){}
}
interface WithNameClass{
new(name:string, age: number): Animal; //类的构造函数是接口的属性
}
function createClass(clazz:WithNameClass, name:string, age: number){
return new clazz(name, age);
}
let a4 = createClass(Animal, 'zhufeng', 18);
console.log(a4); // Animal {name: 'zhufeng', age: 18}
泛型
- 泛型(Generics):在定义函数、接口或者类的时候不预先指定具体的类型,等到使用时再指定具体类型的一种特征。
type A = string | number; //等同于 type A = '' | 1; 定义A支持string和number类型
// let a:A = false //报错:不能将类型“boolean”分配给类型“A”。
let a:A = '哈哈'
let aa:A = 123
type F<T> = T | T[]
type FString = F<string>
type FNumber = F<number>
type Add<K> = (a: K, b: K) => K
// type AddNumber = Add<number>
const AddN: Add<number> = (a,b) => a+b
const AddS: Add<string> = (a,b) => a+''+b
泛型函数
function fun<T> (prop: T): T { // <T>为类型占位符,通过链式传递给参数类型和返回值类型
return prop
}
let a = fun('字符串')
let b = fun(false)
let c = fun([1,'1'])
let d = fun(1)
let e = fun(undefined)
let f = fun(null)
let g = fun(NaN)
泛型接口
interface Cal {
<T>(a: T): T
}
let add: Cal = (a) => {
return a
}
console.log(add('哈哈'))
console.log(add<string>('哈哈'))
//在定义接口的时候也可以指定泛型
interface Cart<T> {
list: T[]
}
let c:Cart<number> = {list: [1,2,3]}
// type可以定义泛型的别名
type Cart2<T> = {list: T[]} | T[]
let c2:Cart2<number> = {list: [3,4]}
let c3:Cart2<number> = [6,7]
泛型类
- 在类名后面跟上类型变量,就能够在类中应用这些泛型类型。
class Person<T, V> {
name: T;
getAge: (age: V) => V
}
let p = new Person<string,number>();
p.name = 'Jake';
p.getAge = (x) => x;
p.getAge(23)
泛型继承(约束泛型)
interface Len {
length: number,
}
function arr<T extends Len> (val: T) {
console.log(val.length)
}
arr([1,2,3])