一、变量的定义
// 数字
const n1: number = 123;
// 布尔
const n2: boolean = true;
// 字符串
const n3: string = 'hello';
// null
const n4: null = null;
// undefined
const n5: undefined = undefined;
// 数组
const names1: Array<string> = ["abc","cba"];
const names2: string[] = ["abc","cba"];
// 对象
const info = {
name: 'why',
age: 18
}
// 符号
const title1 = Symbol("title");
const title2 = Symbol("title");
const sym = {
[title1]: "程序员",
[title2]: "老师",
}
export {}
二、TS 新增的数据类型
1、any 和 unknown
unknown类型只能赋值给any和unknown类型
any类型可以赋值给任意类型
function foo() {
return "abc"
}
function bar() {
return 123
}
// unknown类型只能赋值给any和unknown类型
// any类型可以赋值给任意类型
let flag = true
let result: any // 最好不要使用any
if (flag) {
result = foo()
} else {
result = bar()
}
let message: string = result
let num: number = result
console.log(result)
export {}
2、void
void 通常用来指定一个函数式没有返回值的,那么它的返回值就是void 类型
可以将null 和 undefined 赋值给void 类型,也就是函数可以返回null 和undefined
function sum(num1: number, num2: number) {
console.log(num1 + num2)
}
sum(20, 30)
// sum("abc", "cba")
3、never
function foo(): never {
// 死循环
while(true) {
}
}
function bar(): never {
throw new Error()
}
// 封装一个核心函数
function handleMessage(message: string | number | boolean) {
switch (typeof message) {
case 'string':
console.log("string处理方式处理message")
break
case 'number':
console.log("number处理方式处理message")
break
case 'boolean':
console.log("boolean处理方式处理message")
break
default:
const check: never = message
}
}
handleMessage("abc")
handleMessage(123)
// 张三
handleMessage(true)
4、tuple (元组)
// tuple元组: 多种元素的组合
// "why" 18 1.88
// 1.数组的弊端
const info: any[] = ["why", 18, 1.88]
const infoObj = {
name: "why",
age: 18,
height: 1.88
}
const name = info[0]
console.log(name.length)
// 2.元组的特点
const info1: [string, number, number] = ["why", 18, 1.88]
const name1 = info[0]
console.log(name1.length)
const age = info[1]
console.log(age.length)
export {}
// 元组的应用场景
function useState<T>(state: T) {
let currentState = state
const changeState = (newState: T) => {
currentState = newState
}
const tuple: [T, (newState: T) => void] = [currentState, changeState]
return tuple
}
const [counter, setCounter] = useState(10);
setCounter(1000)
const [title, setTitle] = useState("abc")
const [flag, setFlag] = useState(true)
type MyFunction = () => void
const foo1: MyFunction = () => {}
三、TS类型补充
1、函数的参数和返回值类型
// 给参数加上类型注解: num1: number, num2: number
// 给返回值加上类型注释: (): number
// 在开发中,通常情况下可以不写返回值的类型(自动推导)
function sum(num1: number, num2: number) {
return num1 + num2
}
2、匿名函数的参数类型
// 通常情况下, 在定义一个函数时, 都会给参数加上类型注解的
function foo(message: string) {}
const names = ["abc", "cba", "nba"]
// item根据上下文的环境推导出来的, 这个时候可以不添加类型注解
// 上下文中的函数: 可以不添加类型注解
names.forEach(function(item) {
console.log(item.split(""))
})
3、对象类型
1、对象类型
// Point: x/y -> 对象类型
// {x: number, y: number}
function printPoint(point: {x: number, y: number}) {
console.log(point.x);
console.log(point.y)
}
printPoint({x: 123, y: 321})
export {}
2、可选类型
// Point: x/y/z -> 对象类型
// {x: number, y: number, z?: number}
function printPoint(point: {x: number, y: number, z?: number}) {
console.log(point.x)
console.log(point.y)
console.log(point.z) // undefined
}
printPoint({x: 123, y: 321})
printPoint({x: 123, y: 321, z: 111})
export {}
4、联合类型
// number|string 联合类型
function printID(id: number|string|boolean) {
// 使用联合类型的值时, 需要特别的小心
// narrow: 缩小
if (typeof id === 'string') {
// TypeScript帮助确定id一定是string类型
console.log(id.toUpperCase())
} else {
console.log(id)
}
}
printID(123)
printID("abc")
printID(true)
5、可选类型和联合类型的关系
message?: string -> undefined | string
// 让一个参数本身是可选的
// 一个参数一个可选类型的时候, 它其实类似于是这个参数是 类型|undefined 的联合类型
function foo1(message?: string) {
console.log(message)
}
foo1()
function foo2(message: string|undefined) {
console.log(message)
}
foo2(undefined)
export {}
6、类型别名
// type用于定义类型别名(type alias)
type IDType = string | number | boolean
type PointType = {
x: number
y: number
z?: number
}
function printId(id: IDType) {}
function printPoint(point: PointType) {}
7、类型断言
// <img id="why"/>
// 1.类型断言 as
// 将范围广的类型缩小为具体的类型
const el = document.getElementById("why") as HTMLImageElement
el.src = "url地址"
// 2.另外案例: Person是Student的父类
class Person {}
class Student extends Person {
studying() {}
}
function sayHello(p: Person) {
(p as Student).studying()
}
const stu = new Student()
sayHello(stu)
// 3.了解: as any/unknown
const message = "Hello World"
const num: number = (message as unknown) as number
const num: number = (message as any) as number
8、非空类型断言
// message? -> undefined | string
function printMessageLength(message?: string) {
// if (message) {
// console.log(message.length)
// }
// vue3源码
console.log(message!.length)
// ! 表示message 一定有值时
}
printMessageLength("aaaa")
printMessageLength("hello world")
9、可选链
可选链操作符 ?.
当对象的属性不存在时,会短路,直接返回undefined,如果存在,才会继续执行
type Person = {
name: string
friend?: {
name: string
age?: number,
girlFriend?: {
name: string
}
}
}
const info: Person = {
name: "why",
friend: {
name: "kobe",
girlFriend: {
name: "lily"
}
}
}
// 另外一个文件中
console.log(info.name)
// console.log(info.friend!.name)
console.log(info.friend?.name)
console.log(info.friend?.age)
console.log(info.friend?.girlFriend?.name)
// if (info.friend) {
// console.log(info.friend.name)
// if (info.friend.age) {
// console.log(info.friend.age)
// }
// }
10、字面量类型和字面量推理
// "Hello World"也是可以作为类型的, 叫做字面量类型
const message: "Hello World" = "Hello World"
let num: 123 = 123
// num = 321
// 字面量类型的意义, 就是必须结合联合类型
type Alignment = 'left' | 'right' | 'center'
let align: Alignment = 'left'
align = 'right'
align = 'center'
// align = 'hehehehe'
// 字面量推理
// const info = {
// name: "why",
// age: 18
// }
// info.name = "kobe"
type Method = 'GET' | 'POST'
function request(url: string, method: Method) {}
type Request = {
url: string,
method: Method
}
const options = {
url: "https://www.coderwhy.org/abc",
method: "POST"
} as const
request(options.url, options.method)
四、运算符
1、!!
将其他类型 转成布尔类型
const message = "Hello World"
// const flag = Boolean(message)
// console.log(flag) // true
const flag = !!message
console.log(flag) // true
2、??
空值合并操作符
let message: string|null = "Hello World"
// 如果message 为null 或者是 undefined 时,则使用后面的默认值
const content = message ?? "你好啊, 李银河"
// const content = message ? message: "你好啊, 李银河"
console.log(content)
五、类型缩小
// 1.typeof 的类型缩小
type IDType = number | string
function printID(id: IDType) {
if (typeof id === 'string') {
console.log(id.toUpperCase())
} else {
console.log(id)
}
}
// 2.平等的类型缩小 (=== == !== !=/switch)
type Direction = "left" | "right" | "top" | "bottom"
function printDirection(direction: Direction) {
// 1.if判断
if (direction === 'left') {
console.log(direction)
}
// 2.switch判断
switch (direction) {
case 'left':
console.log(direction)
break;
default:
console.log(direction)
}
}
// 3.instanceof
function printTime(time: string | Date) {
if (time instanceof Date) {
console.log(time.toUTCString())
} else {
console.log(time)
}
}
class Student {
studying() {}
}
class Teacher {
teaching() {}
}
function work(p: Student | Teacher) {
if (p instanceof Student) {
p.studying()
} else {
p.teaching()
}
}
const stu = new Student()
work(stu)
// 4. in
type Fish = {
swimming: () => void
}
type Dog = {
running: () => void
}
function walk(animal: Fish | Dog) {
if ('swimming' in animal) {
animal.swimming()
} else {
animal.running()
}
}
const fish: Fish = {
swimming() {
console.log("swimming")
}
}
walk(fish)
六、函数类型
1、函数的类型
// 1.函数作为参数时, 在参数中如何编写类型
function foo() {}
type FooFnType = () => void
function bar(fn: FooFnType) {
fn()
}
bar(foo)
// 2.定义常量时, 编写函数的类型
type AddFnType = (num1: number, num2: number) => number
const add: AddFnType = (a1: number, a2: number) => {
return a1 + a2
}
function calc(n1: number, n2: number, fn: (num1: number, num2: number) => number) {
return fn(n1, n2)
}
const result1 = calc(20, 30, function(a1, a2) {
return a1 + a2
})
console.log(result1)
const result2 = calc(20, 30, function(a1, a2) {
return a1 * a2
})
console.log(result2)
2、函数参数的可选类型
可选类型必须写在必选类型的后面
// 可选类型是必须写在必选类型的后面的
// y -> undefined | number
function foo(x: number, y?: number) {}
foo(20, 30)
foo(20)
3、函数参数的默认值
// 必传参数 - 有默认值的参数 - 可选参数
// 一般将有默认值的参数放到后面
function foo(y: number, x: number = 20) {
console.log(x, y)
}
foo(30)
// 当不传第一个参数时,需要指定为undefined
function foo(x: number = 20, y: number) {
console.log(x, y)
}
foo(undefined, 20)
4、函数的剩余参数
// function sum(num1: number, num2: number) {
// return num1 + num2
// }
function sum(initalNum: number, ...nums: number[]) {
let total = initalNum
for (const num in nums) {
console.log("num", num)
}
return total
}
console.log(sum(20, 30))
console.log(sum(20, 30, 40))
console.log(sum(20, 30, 40, 50))
5、this 的默认推导
// this是可以被推导出来 info对象(TypeScript推导出来)
const info = {
name: "why",
eating() {
console.log(this.name + " eating")
}
}
info.eating()
6、this 的不明确类型
type ThisType = { name: string };
function eating(this: ThisType, message: string) {
console.log(this.name + " eating", message);
}
const info = {
name: "why",
eating: eating,
};
// 隐式绑定
info.eating("哈哈哈");
// 显示绑定
eating.call({name: "kobe"}, "呵呵呵")
eating.apply({name: "james"}, ["嘿嘿嘿"])
export {};
7、函数的承载
1、联合类型出现的问题
/**
* 通过联合类型有两个缺点:
* 1.进行很多的逻辑判断(类型缩小)
* 2.返回值的类型依然是不能确定
*/
function add(a1: number | string, a2: number | string) {
if (typeof a1 === "number" && typeof a2 === "number") {
return a1 + a2
} else if (typeof a1 === "string" && typeof a2 === "string") {
return a1 + a2
}
// return a1 + a2;
}
add(10, 20)
2、函数的重载
// 函数的重载: 函数的名称相同, 但是参数不同的几个函数, 就是函数的重载
// 函数的声明
function add(num1: number, num2: number): number; // 没函数体
function add(num1: string, num2: string): string;
// 函数的实现
function add(num1: any, num2: any): any {
if (typeof num1 === 'string' && typeof num2 === 'string') {
return num1.length + num2.length
}
return num1 + num2
}
const result = add(20, 30)
const result2 = add("abc", "cba")
console.log(result)
console.log(result2)
// 在函数的重载中, 实现函数是不能直接被调用的
// add({name: "why"}, {age: 18})
export {}
3、函数重载的练习
// 实现方式一: 联合类型
function getLength(args: string | any[]) {
return args.length
}
console.log(getLength("abc"))
console.log(getLength([123, 321, 123]))
// 实现方式二: 函数的重载
// function getLength(args: string): number;
// function getLength(args: any[]): number;
// function getLength(args: any): number {
// return args.length
// }
// console.log(getLength("abc"))
// console.log(getLength([123, 321, 123]))
七、类的使用
1、类的定义
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
eating() {
console.log(this.name + " eating")
}
}
const p = new Person("why", 18)
console.log(p.name)
console.log(p.age)
p.eating()
2、类的继承
class Person {
name: string = ""
age: number = 0
eating() {
console.log("eating")
}
}
class Student extends Person {
sno: number = 0
studying() {
console.log("studying")
}
}
class Teacher extends Person {
title: string = ""
teaching() {
console.log("teaching")
}
}
const stu = new Student()
stu.name = "coderwhy"
stu.age = 10
console.log(stu.name)
console.log(stu.age)
stu.eating()
class Person {
name: string
age: number
// 构造器
constructor(name: string, age: number) {
this.name = name
this.age = age
}
eating() {
console.log("eating 100行")
}
}
class Student extends Person {
sno: number
constructor(name: string, age: number, sno: number) {
// super调用父类的构造器
super(name, age)
this.sno = sno
}
eating() {
console.log("student eating")
super.eating()
}
studying() {
console.log("studying")
}
}
const stu = new Student("why", 18, 111)
console.log(stu.name)
console.log(stu.age)
console.log(stu.sno)
stu.eating()
3、类的多态
class Animal {
action() {
console.log("animal action")
}
}
class Dog extends Animal {
action() {
console.log("dog running!!!")
}
}
class Fish extends Animal {
action() {
console.log("fish swimming")
}
}
// 多态:父类引用(类型)指向子类对象
// animal: dog/fish
// 多态的目的是为了写出更加具备通用性的代码
function makeActions(animals: Animal[]) {
animals.forEach(animal => {
animal.action()
})
}
makeActions([new Dog(), new Fish()])
4、类的成员修饰符
1、public
修饰的是在任何地方可见、公有的属性或方法,默认编写的属性就是public
2、private
修饰的仅是同一类中可见、私有的属性或方法
class Person {
private name: string = ""
// 封装了两个方法, 通过方法来访问name
getName() {
return this.name
}
setName(newName) {
this.name = newName
}
}
const p = new Person()
console.log(p.getName())
p.setName("why")
3、prrotected
修饰的是仅在类自身及子类中可见、受保护的属性或方法
// protected: 在类内部和子类中可以访问
class Person {
protected name: string = "123"
}
class Student extends Person {
getName() {
return this.name
}
}
const stu = new Student()
console.log(stu.getName())
5、只读属性_readonly
class Person {
// 1.只读属性是可以在构造器中赋值, 赋值之后就不可以修改
// 2.属性本身不能进行修改, 但是如果它是对象类型, 对象中的属性是可以修改
readonly name: string
age?: number
readonly friend?: Person
constructor(name: string, friend?: Person) {
this.name = name
this.friend = friend
}
}
const p = new Person("why", new Person("kobe"))
console.log(p.name)
console.log(p.friend)
// 不可以直接修改friend
// p.friend = new Person("james")
if (p.friend) {
p.friend.age = 30
}
// p.name = "123"
6、类的访问器:getters-setters
class Person {
private _name: string
constructor(name: string) {
this._name = name
}
// 访问器setter/getter
// setter
set name(newName) {
this._name = newName
}
// getter
get name() {
return this._name
}
}
const p = new Person("why")
p.name = "coderwhy"
console.log(p.name)
7、类的静态成员
// class Person {
// name: string = ""
// age: number = 12
// }
// const p = new Person()
// p.name = "123"
class Student {
static time: string = "20:00"
static attendClass() {
console.log("去学习~")
}
}
console.log(Student.time)
Student.attendClass()
8、抽象类
抽象类是不能实例化的(也就是不能通过new 创建)
抽象方法必须被子类实现,否则该类必须是一个抽象类
function makeArea(shape: Shape) {
return shape.getArea()
}
// 抽象类中的抽象方法必须被子类实现
abstract class Shape {
abstract getArea(): number
}
class Rectangle extends Shape {
private width: number
private height: number
constructor(width: number, height: number) {
super()
this.width = width
this.height = height
}
getArea() {
return this.width * this.height
}
}
class Circle extends Shape {
private r: number
constructor(r: number) {
super()
this.r = r
}
getArea() {
return this.r * this.r * 3.14
}
}
const rectangle = new Rectangle(20, 30)
const circle = new Circle(10)
console.log(makeArea(rectangle))
console.log(makeArea(circle))
// makeArea(new Shape())
// makeArea(123)
// makeArea("123")
9、类的类型
class Person {
name: string = "123"
eating() {
}
}
const p = new Person()
const p1: Person = {
name: "why",
eating() {
}
}
function printPerson(p: Person) {
console.log(p.name)
}
printPerson(new Person())
printPerson({name: "kobe", eating: function() {}})
八、接口的使用
1、声明对象类型
// 通过类型(type)别名来声明对象类型
// type InfoType = {name: string, age: number}
// 另外一种方式声明对象类型: 接口interface
// 在其中可以定义可选类型
// 也可以定义只读属性
interface IInfoType {
readonly name: string
age: number
friend?: {
name: string
}
}
const info: IInfoType = {
name: "why",
age: 18,
friend: {
name: "kobe"
}
}
console.log(info.friend?.name)
console.log(info.name)
// info.name = "123"
info.age = 20
2、索引类型
// 通过interface来定义索引类型
interface IndexLanguage {
[index: number]: string
}
const frontLanguage: IndexLanguage = {
0: "HTML",
1: "CSS",
2: "JavaScript",
3: "Vue"
}
interface ILanguageYear {
[name: string]: number
}
const languageYear: ILanguageYear = {
"C": 1972,
"Java": 1995,
"JavaScript": 1996,
"TypeScript": 2014
}
3、函数类型
// type CalcFn = (n1: number, n2: number) => number
// 可调用的接口
interface CalcFn {
(n1: number, n2: number): number
}
function calc(num1: number, num2: number, calcFn: CalcFn) {
return calcFn(num1, num2)
}
const add: CalcFn = (num1, num2) => {
return num1 + num2
}
calc(20, 30, add)
4、接口的继承
interface ISwim {
swimming: () => void
}
interface IFly {
flying: () => void
}
// 接口实现多继承
interface IAction extends ISwim, IFly {
}
const action: IAction = {
swimming() {
},
flying() {
}
}
5、交叉类型
// 一种组合类型的方式: 联合类型
type WhyType = number | string
type Direction = "left" | "right" | "center"
// 另一种组件类型的方式: 交叉类型
type WType = number & string
interface ISwim {
swimming: () => void
}
interface IFly {
flying: () => void
}
type MyType1 = ISwim | IFly
type MyType2 = ISwim & IFly
const obj1: MyType1 = {
flying() {
}
}
const obj2: MyType2 = {
swimming() {
},
flying() {
}
}
6、接口的实现
interface ISwim {
swimming: () => void
}
interface IEat {
eating: () => void
}
// 类实现接口
class Animal {}
// 继承: 只能实现单继承
// 实现: 实现接口, 类可以实现多个接口
class Fish extends Animal implements ISwim, IEat {
swimming() {
console.log("Fish Swmming")
}
eating() {
console.log("Fish Eating")
}
}
class Person implements ISwim {
swimming() {
console.log("Person Swimming")
}
}
// 编写一些公共的API: 面向接口编程
function swimAction(swimable: ISwim) {
swimable.swimming()
}
// 1.所有实现了接口的类对应的对象, 都是可以传入
swimAction(new Fish())
swimAction(new Person())
swimAction({swimming: function() {}})
7、interface 和 type 的区别
如果是定义非对象类型,建议使用type
如果是定义对象类型,他们是有区别的:
1.interface 可以重复的对某个接口来定义属性和方法
2.而type 定义的是别名,别名是不能重复的
interface IFoo {
name: string
}
interface IFoo {
age: number
}
// interface IFoo {
// name: string
// age: number
// }
// 同名的接口,里面的属性是合并关系
const foo: IFoo = {
name: "why",
age: 18
}
document.getElementById("app") as HTMLDivElement
window.addEventListener
interface Window {
age: number
}
window.age = 19
console.log(window.age)
// type 不能定义名字重复的类型
type IBar = {
name: string
}
// type IBar = {
// age: number
// }
8、字面量赋值
interface IPerson {
name: string
age: number
height: number
}
const info = {
name: "why",
age: 18,
height: 1.88,
address: "广州市"
}
// freshness擦除
const p: IPerson = info
console.log(info)
console.log(p)
function printInfo(person: IPerson) {
console.log(person)
}
// 代码会报错
// printInfo({
// name: "why",
// age: 18,
// height: 1.88,
// address: "广州市"
// })
const info = {
name: "why",
age: 18,
height: 1.88,
address: "广州市"
}
printInfo(info)
九、枚举类型
// type Direction = "left" | "Right" | "Top" | "Bottom"
enum Direction {
LEFT = "LEFT", // 默认是 0
RIGHT = "RIGHT", // 默认是 1
TOP = "TOP", // 默认是 2
BOTTOM = "BOTTOM" // 默认是 3
}
// let name: string = "abc"
// let d: Direction = Direction.BOTTOM
function turnDirection(direction: Direction) {
console.log(direction)
switch (direction) {
case Direction.LEFT:
console.log("改变角色的方向向左")
break;
case Direction.RIGHT:
console.log("改变角色的方向向右")
break;
case Direction.TOP:
console.log("改变角色的方向向上")
break;
case Direction.BOTTOM:
console.log("改变角色的方向向下")
break;
default:
const foo: never = direction;
break;
}
}
turnDirection(Direction.LEFT)
turnDirection(Direction.RIGHT)
turnDirection(Direction.TOP)
turnDirection(Direction.BOTTOM)
十、泛型
T: Type 的缩写, 类型
K、V: key 和 value 的缩写, 键值对
E:Element 的缩写,元素
O: Object 的缩写,对象
1、泛型的使用
// 类型的参数化
// 在定义这个函数时, 我不决定这些参数的类型
// 而是让调用者以参数的形式告知,我这里的函数参数应该是什么类型
function sum<Type>(num: Type): Type {
return num
}
// 1.调用方式一: 明确的传入类型
sum<number>(20)
sum<{name: string}>({name: "why"})
sum<any[]>(["abc"])
// 2.调用方式二: 类型推到
sum(50)
sum("abc")
2、泛型接收多个参数
function foo<T, E, O>(arg1: T, arg2: E, arg3?: O, ...args: T[]) {
console.log(arg1, arg2, arg3, args)
}
foo<number, string, boolean>(10, "abc", true, 100, 200, 300)
3、泛型接口的使用
interface IPerson<T1, T2> {
name: T1
age: T2
}
const p: IPerson<string, number> = {
name: "why",
age: 18
}
interface IPerson<T1=string, T2=number> {
name: T1
age: T2
}
const p: IPerson= {
name: "why",
age: 18
}
4、泛型类的使用
class Point<T> {
x: T
y: T
z: T
constructor(x: T, y: T, z: T) {
this.x = x
this.y = y
this.z = z
}
}
const p1 = new Point("1.33.2", "2.22.3", "4.22.1")
const p2 = new Point<string>("1.33.2", "2.22.3", "4.22.1")
const p3: Point<string> = new Point("1.33.2", "2.22.3", "4.22.1")
const names1: string[] = ["abc", "cba", "nba"]
const names2: Array<string> = ["abc", "cba", "nba"] // 不推荐(react jsx <>)
5、泛型的类型约束
interface ILength {
length: number
}
function getLength<T extends ILength>(arg: T) {
return arg.length
}
getLength("abc")
getLength(["abc", "cba"])
getLength({length: 100})
6、非空判断运算符
const flag = "" ?? true
console.log(flag)
十一、ts 补充
1、命名空间
// format.ts
export namespace time {
export function format(time: string) {
return "2222-02-22"
}
export function foo() {
}
export let name: string = "abc"
}
export namespace price {
export function format(price: number) {
return "99.99"
}
}
// main.ts
import { time, price } from './utils/format'
console.log(time.format("11111111"))
console.log(price.format(123))
2、模块和声明
// 声明模块
declare module 'lodash' {
export function join(arr: any[]): void
}
// 声明变量/函数/类
declare let whyName: string
declare let whyAge: number
declare let whyHeight: number
declare function whyFoo(): void
declare class Person {
name: string
age: number
constructor(name: string, age: number)
}
// 声明文件
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.png'
declare module '*.svg'
declare module '*.gif'
// 声明命名空间
declare namespace $ {
export function ajax(settings: any): any
}