解释:
结论:js的特点是灵活、高效,很短的代码就能实现复杂功能,缺点是没有代码提示,容易出错且编辑工具不会给任何提示
,而Java则相反。
TS可以简单理解为将JavaScript转变为Java一类语言的过程。
TS属于静态类型的变成语言,JS属于动态类型的编程语言
。
编译期做类型检查
执行期做类型检查
并且,配合VSCode等开发工具,TS可以提前到在编写代码的同时就发现代码中的错误
,减少找Bug,改Bug时间,另外还支持代码提示
。
Vue3的源码使用TS重写,Angular默认支持TS、React与TS完美配合,TypeScript已经成为大中型前端项目的首选变成语言。
注意:Vue2对TS的支持不好~
类型注解总结:
.
可以直接看到当前类型所支持的所有属性和方法
可以将TS中的常用基础类型细分为两类:
JS已有类型
TS新增类型
// 字符串类型
let a:string = '123';
let arr:number[] = [123]
//这样会报错定义了数字类型出现字符串是不允许的
let arr:number[] = [1,2,3,'1']
//数字类型的数组
var arr: number[] = [1, 2, 3];
//字符串类型的数组
var arr2: string[] = ["1", "2"];
// 数组泛型,规则 Array<类型>
let arr:Array<number> = [1, 2, 3, 4, 5];
// 多维数组
let data:number[][] = [[1,2], [3,4]];
//任意类型的数组
var arr3: any[] = [1, "2", true];
// 希望数组里面可以存数字或字符串
// 联合类型 |
let arr4:(number | string)[] = [1, 2, 4, '2323'];
// 注意:| 的优先级较低,需要用()包裹提升优先级
// 一旦使用联合类型,说明 arr 中存储的既可能是number 也可能是 string,所以会丢失一部分提示信息
// 定时器?
目标:能够使用类型别名给类型起别名。
使用场景:当同一类型(复杂)被多次使用时,可以通过type
定义类型别名(推荐使用大写字母开头),简化该类型的使用。
// 希望 N 个数组里面可以存数字或字符串
// 方法1:里面上面的联合类型
// 方法2:类型别名
type ArrType = (number | string)[]
let arr1: ArrType = ['1', 2, 4, '2323'];
let arr2: ArrType = [1, '2', 4, '2323'];
let arr3: ArrType = [1, 2, '4', '2323'];
// 灵活度很高,可以随意搭配组合使用
type ItemType = number | string
let arr4: ItemType[] = [1, 2, '3', '123'];
let arr5: ItemType = '23'
// 总结:将一组类型存储到【变量】里,用 type 来声明这个特殊的【变量】
// 玩花活
type s = string
type n = number
let str2: s = '123'
let num2: n = 123
函数的类型实际上指的是:函数参数
和返回值
的类型
为函数指定类型的两种方式:
// ts 要求我们必须给参数定义类型,而返回值它会自动推断
// 函数声明
function add(a: number, b: number) {
return a + b;
}
// 箭头函数,ts中的箭头函数必须是小括号
const sub = (a: number, b: number): number => {
return a + b;
}
// 函数的类型别名
type FnType = (a: number, b: number) => number
// 函数的类型别名通常是给箭头函数/函数表达式使用,不会给函数声明使用
const fn: FnType = function(a, b) {
return a + b
}
如果函数没有返回值,那么,在TS的类型中,函数返回值类型为void
const sayHello = (content: string): void => {
console.log(content)
}
// 但,如果指定返回值类型为 undefined,此时,函数体中必须显示的 return undefined 才可以
const addN = (): undefined => {
return undefined
}
?
const printP = (name: string, age?: number): void => {
console.log(name, age)
}
printP('哆啦A梦', 18)
printP('波多野结衣')
JS中的对象是由属性和方法构成的,而TS对象的类型就是在描述对象的结构
(有什么类型的属性和方法)。
调用对象时可以直接提示有哪些属性和方法
// ts 就像咋写注释,以前写的注释是给程序员看的,ts 写的类型是给编辑器看的,程序员也可以看
let obj: {
name: string,
age: number,
sayHi: (content: string) => void
} = {
name: '哆啦A梦',
age: 18,
sayHi(content) {
console.log(content)
}
}
// 类型别名用法
// 对象类型
type Person = {
name: string,
age: number,
sayHi: (content: string) => void
}
let obj1: Person = {
name: '哆啦A梦',
age: 18,
sayHi(content) {
console.log(content)
}
}
方法类型的两种定义形式
// 方法的类型也可以使用箭头函数形式
type Person = {
greet: (name: string) => void
// greet(name: string) => void
}
let person: Person = {
greet(name) {
console.log(name)
}
}
当一个对象类型多多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的。
作用:给对象约束属性和方法
// 基础用法:
interface IPerson {
name: string
age: number
sayHi: () => void
}
const p1: IPerson = {
name: '苍老师',
age: 18,
sayHi() {
console.log('i like')
}
}
interface
关键字声明接口I
开头总结:
interface 和 type 的区别,接口interface
只能约束对象,而类型别名 type
不仅可以为对象指定类型,实际上可以为任何类型指定别名。
推荐:能使用type就使用type
,更灵活,更简单
如果两个接口之间有相同的属性和方法,可以将公共的属性和方法抽离出来,通过继承来实现复用
。
// 比如,这两个接口都有x、y两个属性,重复写两次,可以,但是很繁琐
interface Point2D {
x: number
y: number
}
interface Point3D {
x: number
y: number
z: number
}
// 更好的方式
interface Point2D {
x: number
y: number
}
// 继承Point2D
interface Point3D extends Point2D {
z: number
}
type Person = {
usename: string
age: number
sayHi: () => void
}
// & 与连接符:既要满足前面的也要满足后面的
// | 或连接符:满足其中一个即可
type Student = {
score: number
sleep: () => void
} & Person
接口继承:可以实现让一个接口使用另一个接口的类型约束,实现接口的复用。
限定数组的固定类型和数据长度
,元组类型时另一种类型的数组,他确切地知道包含多少个元素,以及特定索引对应的类型。
// 地图经纬度
let arr6: [number, number] = [13.334, 36.2323]
字面量类型就是把字面量当做类型来用。eg:
let s1 = 'hello TS';
const s2 = 'hello TS';
通过TS类型推论机制,可以得到:s1的类型时string,s2的类型为 ‘hello TS’;
解释:s2是一个常量,它的值不能变化只能是 ‘hello TS’,所以,它的类型为 ‘hello TS’。此次 ‘hello TS’,就是一个字面量类型
,也就是说某个特定的字符串也可以作为TS中的类型
。
比如:在贪吃蛇游戏中,游戏的方向的可选值只能是上、下、左、右中的任意一个
枚举的功能类似于字面量类型+联合类型组合
的功能,也可以表示一组明确的可选值;
枚举:定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个
如果不设置值,默认从 0 开始
// 创建枚举
enum Direction {
Up,
Dowm,
Left,
Right
}
// 使用枚举类型
function changeDirection(direction: Direction) {
console.log(direction)
}
enum
关键字定义枚举大写字母
开头,
(逗号)分割我们把枚举成员的值为数字的枚举,称为数字枚举
。注意:枚举成员是有值的,默认为:从0开始自增的数值
// 给枚举中的成员初始化值
enum Direction {
Up = 10,
Dowm,
Left,
Right
}
// Dowm => 11, Left => 12, Right => 13
枚举成员的值为字符串。字符串枚举没有自增长的行为,因此,字符串枚举的每个成员必须有初始值。
enum Direction {
Up = 'Up',
Dowm = 'Dowm',
Left = 'Left',
Right = 'Right'
}
枚举是TS为数不多的非JavaScript类型级扩展的特性之一
其他类型仅仅被当做类型,而枚举不仅用作类型,还提供值
(枚举成员都是有值的)
也就是说,其他的类型会在编译为JavaScript代码时自动移除,但是 枚举类型会被编译为JS 代码
// 会被编译为以下代码:
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Dowm"] = 1] = "Dowm";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
说明:枚举与前面安静到的字面量类型+联合类型
组合的功能类似,都用来表示一组明确的可选值列表。
一般情况下,推荐字面量类型 + 联合类型
组合的方式,因为相比枚举,这种方式更加直观、简洁、高效。
any类型:原则上不推荐使用
,只有在迫不得已的时候才可以用一下,否则会变成 AnyScript,失去 TS 类型保护机制
let obj2: any = {x: 0}
const n: number = obj2
尽可能的避免使用any类型,除非临时使用any来避免书写很长、很复杂的类型,其他隐式具有any类型的情况
注意:因为不推荐使用any,所以,这两种情况下都应该提供类型
类型断言:强行指定获取到的结果类型
有时候你会比TS更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型,eg:
const aLink = document.getElementById('link')
注意:该方法返回值的类型是HTMLElement
,该类型只包含所有标签公共的属性和方法,不包含a标签特有的href等属性,因此,这个类型太宽泛
,无法操作href等a标签特有的属性和方法
解决办法:这种情况下就需要使用类型断言指定更加具体的类型。
const aLink = document.getElementById('link') as HTMLAnchorElement
// aLink.href, 敲代码段的时候直接有提示
as
关键字实现类型断言as
后面的类型时一个更加具体的类型 (HTMLAnchorElement)另一种写法,使用<> 语法,不常用,知道即可
const alink2 =
总结:当函数获取到的结果类型较为宽泛时,我们有知道具体类型,就可以使用断言强行指定类型。
泛型是可以保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用
,常用于:函数、接口、class中。
泛型在保证类型安全(不丢失类型信息的同时,可以让函数等与多种不同的类型一起工作,灵活可复用。)
// 定义一个getId,传入一个值,返回这个值,可以传入任意类型
// any 可以解决问题,但也会带来问题:返回值也是any没有提示了
function getId(val: number | string | boolean) {
return val
}
解决方案:
// 期望:调用getId函数时来指定传入参数的类型
// 在声明泛型
// (val: T) 使用泛型
// 调用函数时传入泛型指定的具体类型
function getId<T>(val: T) {
return val
}
console.log(getId<number>(123))
console.log(getId<string>('123'))
// 简化泛型函数调用, 也可以使用,调用时不用加<>
console.log(getId(123))
上面的T和下面的Type是一样的,知识变量名称不同罢了
// 如下代码:当传输string,正确,当输入number时,报错,所以需要对val进行约束,即:泛型约束
function getId<T>(val: T) {
console.log(val.length)
return val
}
添加约束(常见方法)
// 定义接口
interface ILength {
length: number
}
// 使用,添加约束,给泛型找爸爸
function getId<T extends ILength>(val: T) {
console.log(val.length) // 输入val代码提示length属性
return val
}
泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如,第二个类型变量受第一个类型变量约束)
// 需求,定义一个函数,传入一个对象,在传入一个字符串属性名,返回属性值
function getProp(obj: object, key: string) {
return obj[key] // 这种方式会报错,不知道是否这个key
}
function getProp<O, K extends keyof O>(obj: O, key: K) {
return obj[key]
}
let person = {name: 'jack', age: 18}
getProp(person, 'name')
interface Student {
id: number
name: string
hobby: string[]
}
let s1: Student = {
id: 123,
name: '波多小姐',
hobby:['瑜伽', '运动', '歌唱']
}
// 转化
interface Student<T> {
id: number
name: T
hobby: T[]
}
let s1: Student<string> = {
id: 123,
name: '波多小姐',
hobby:['瑜伽', '运动', '歌唱']
}
参考:
链接