TypeScript
TypeScript(简称: TS)是JavaScript的超集 (JS有的 TS 都有)
TypeScript = Type +JavaScript (在JS 基础之上,为Js 添加了类型支持)
TypeScript是微软开发的开源编程语言,可以在任何运行JavaScript的地方运行
// TypeScript 代码: 有明确的类型,即 : number(数值类型)
let age:number = 18
// JavaScript 代码: 无明确的类型
let age = 18
TypeScript介绍
从编程语言来区分,TypeScript届于静态类型的编程语言,JS届于动态类型的编程语言
静态类型:编译期做类型检查,动态类型:执行期做类型检查
代码编译和代码执行的顺序:1 编译 2执行
对于JS来说:需要等到代码真正去执行的时候才能发现错误(晚)。
对于 TS 来说:在代码编译的时候(代码执行前)就可以发现错误 (早)
TypeScript使用
安装编译TS的工具包
npm i -g typescript
// 验证是否编译成功 tsc -v
终端内运行ts代码
两种方法
1.
tsc xxx.ts // 将ts文件编译成js
node xxx.js // 运行
2.
npm i -g ts-node // 下包
ts-node xxx.ts // 运行
// 报错解决:用tsc --init命名,在根目录下生成配置文件 tsconfig.json
TypeScript常用类型
类型注解
: number就是类型注解,为变量添加类型约束
为变量指定类型
原始类型
let age: number = 18 // 数字型
let age: string = '18' // 字符串型
let age: boolean = false // 布尔型
let age: null = null // null
let age: undefined = undefined // undefined
let age: symbol = Symbol() // symbol
数组类型
let age: number[] = [1,2,3] // 数值数组
let age: string[] = ['1','2','3'] // 字符串数组
// 另一种写法 let age: Array<string> = ['1','2','3']
联合类型
由两个或多个类型组成的
let age: (number | string)[] = [1,'2',3] // 数字和字符串数组
let age: boolean[] = [true,false] // 布尔数组
元组
是另一种类型的数组,确切的知道包含多少个元素及对应类型
let arr:[number,number] = [1,2]
对象类型
对象的类型就是在描述对象的结构,属性类型之间用 ;分隔
属性采用 属性名:类型 ,方法采用 方法名:返回值类型,的形式
let obj:{name: string; age: number; sayHi: void} = {
name:'a',
age:18,
sayHi(){ }
}
函数类型
实际上是给函数的参数和返回值指定类型
// 单独指定参数和返回值类型
function add(num1:number,num2:number): number {
return num1 + num2
}
function add = (num1:number,num2:number):返回值类型 =>{
return num1 + num2
}
// 同时指定参数和返回值类型 只适用于函数表达式
const add:(num1:number,num2:number) => number=(num1,num2)=>{
return num1 + num2
}
// 传一个对象
function add(config: {url: string; method?: string}) {
console.log('hello')
}
void类型
如果函数没有返回值return,那么返回值类型为 void
function add(name:string): void {
console.log('hello')
}
可选参数
参数可以传也可以不传, 参数名称后添加 ?
可选参数只能出现在参数列表的最后
function add(name?:string): void {
console.log('hello')
}
类型别名 (自定义类型)
type(类型别名) interface(接口)
为任意类型起别名,简化该类型的使用
两种 type(类型别名) 和 interface(接口) ,interface 只能为对象指定类型
type CustomArray = (number | string)[] // 创建别名,可以是任意变量名
let age: CustomArray = [1,'2',3] // 使用别名作为类型注解
interface objty { // 创建对象别名
name: string
age: number
}
let obj:objty = { // 使用别名作为类型注解
name:'123',
age:18
}
接口继承 extends
extends
interface P2 {x:number; y:number}
// P3继承P2的类型并添加自己的类型
interface P3 extends P2 {z:number}
// P3= {x:number; y:number; z:number}
类型断言 as
使用as关键字实现类型断言
const aLink = document.getElementById('link') as HTMLAnchorElement
// 通过类型断言 alink的类型变得更加具体,这样就可以访问a标签特有的属性和方法了
字面量类型
某个特定的字符串也可以作为TS中的类型
let str1 = 'hello' // 他的类型为 字符串
const str2 = 'hello' // 他的类型为 hello 就是一个字面量类型
// 因为 str2 是一个常量,值不能变化只能是hello,所以它的类型为 hello
// 除字符串外,任意js字面量(比如对象,数字等)都可以作为类型使用
枚举类型 enum
enum关键字
枚举:定义一组命名常量,描述一个值,该值可以是这些命名常量中的一个
枚举中的值以大写字母开头,多个值以逗号分割
enum Direction {Up, Down, Left, Right}
function changeDirection(direction: Direction) {
console.log(direction)
}
// 访问枚举成员 Direction.Up
// 调用
changeDirection(Direction.Up)
数字枚举
// 枚举成员是有值的,默认从0开始自增的数值
// 也可以给枚举中成员初始化值
enum Direction {Up=10, Down, Left, Right}
// 如此Down=11 以此类推自增
字符串枚举
enum Direction {Up='up', Down='Down'}
// 字符串枚举是没有自增长行为的,并且每个成员必须都有值
any类型
不推荐用,会让typescript 变成 anyscript 并没有任何错误提示
let obj:any = {x:0}
typeof运算符
用来在js中获取数据类型
也可以在类型上下文中引用变量或对象.属性的类型(类型查询)
let p = {x:1, y:2}
// 两种写法意思相同
function format(point:{x:number; y: number}) {}
function format(point: typeof p) {} // 就会把p的类型引用下来
TypeScript高级类型
class类
不仅提供了js中class语法的功能,也作为一种类型存在
class Person {} // 创建一个类
const p = new Person() // 创建实例对象
class类的基本使用
class Person { // 创建一个类
age:number
gender='男'
}
const p = new Person() // 创建实例对象
p.age // 使用
class类的构造函数 constructor
constructor
class Person { // 创建一个类
age:number
gender:string
// 构造函数
constructor(age: number, gender: string) {
this.age=age
this.gender=gender
}
}
const p = new Person(18, '男')
class类的继承 extends implements
extends (继承父类)
implements (实现接口)
// extends
class Animal {
move() { log('1') }
}
class Dog extends Animal { // 继承父类里所有属性和方法
bark() { log('2') }
}
const d = new Dog()
dog.move() // 可以访问父类里的方法
// implements
class Animal {
move(): void
}
class Dog implements Animal {
// 里面必须提供Animal里的所有属性和方法,
move() {
log('2')
}
}
class类的可见性修饰符
public (公有的)
表示公开的,可以被任何地方访问,默认可见性可省略
class Animal {
public move(){
log(1)
}
}
const a = new Animal()
a.move() // 1
protected (受保护的)
表示受保护的,仅对其声明所在类和子类中可见,实例对象不可见
可以在子类中通过this访问受保护的
class Animal {
protected move(){ log(1) }
run(){
this.move() // 在类中通过this调用
}
}
const a = new Animal()
a.move() // 报错
class Dog extends Animal {
bark(){
this.move() // 在子类中通过this调用
}
}
const d = new Dog()
d.bark() // 1
private (私有的)
表示私有的,只在当前类中可见
class Animal {
private move(){ log(1) }
run(){
this.move() // 在当前类的其他方法中通过this调用
}
}
class类的只读修饰符
readonly
表示只读,用来防止在构造函数之外对属性进行赋值
只能修饰属性,不能修饰方法
接口或者{}表示的对象类型,也可以使用readonly
class Animal {
// 只读属性
readonly age:number = 18 // 需指定类型 否则无法赋值
constructor(age:number){
this.age= age // 只能在构造函数中赋值
}
run() {
this.age= 20 // 方法中赋值 报错
}
}
const a = new Animal(20)
类型兼容性
两种类型系统:
1.Structural Type System (结构化类型系统)
2.Nominal Type System (标明类型系统)
ts采用的是结构化类型系统,也叫做鸭子类型
对象之间的类型兼容性
对于对象类型来说,成员多的可以赋值给成员少的
// Point兼容Point2D
class Point { x:number; y:number }
class Point2D { x:number; y:number; z:number }
const p:Point = new Point2D() // 可以
const p:Point2D = new Point() // 报错
接口之间的类型兼容性
类似于class
class 和 interface 都是对象,所以他俩也可以兼容
interface Point { x:number; y:number; }
interface Point2D { x:number; y:number; }
class Point3D { x:number; y:number; z:number }
let p1:Point
let p2:Point2D
p2=p1
p1=p2
let p3:Point2D = new Point3D() // 也可以兼容
函数之间的类型兼容性
函数兼容比较复杂,需要考虑 参数个数、参数类型、返回值类型
参数个数
参数少的可以赋值给参数多的
type F1 = (a:number) => void
type F2 = (a:number,b:number) => void
let f1:F1
let f2:F2
f2 = f1 // 可以
参数类型
相同位置的参数类型要相同(原始类型)或兼容(对象类型)
type F1 = (a:number) => void
type F2 = (a:number) => void
let f1:F1
let f2:F2
f2 = f1 // 可以
f1 = f2 // 可以
interface Ponint2D { x:number; y:number;}
interface Ponint3D { x:number; y:number; z:number;}
// 技巧 :将对象拆开,把每个属性个数看做一个个参数
type F2 = (a:Ponint2D) => void // 相当于有2个参数
type F3 = (a:Ponint3D) => void // 相当于有3个参数
let f2:F2
let f3:F3
f2=f3 // 不行
f3=f2 // 可以 参数少的可以赋值给参数多的
返回值类型
只关注返回值类型本身
// 对象类型 成员多的可以赋值给成员少的
type F1 = () => {name:string}
type F2 = () => {name:string; age:number}
let f1:F1
let f2:F2
f1 = f2 // 可以
// 原始类型 返回的类型相同就可以兼容
type F1 = () => string
type F2 = () => string
let f1:F1
let f2:F2
f1 = f2 // 可以
f2 = f1 // 可以
交叉类型 &
交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为1个类型(常用于对象类型)
interface Person { name: string}
interface Contact { phone: string}
type PersonDetail = Person & Contact
//
let obj:PersonDetail = {
name: 'abc',
phone: '123'
}
交叉类型和接口之间的对比
相同点:都可以实现对象类型的组合
不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同
// 接口
interface A {
fn:(value: number)=> string
}
interface B extends A {
fn:(value: string)=> string
}
// 报错 (类型不兼容) 接口继承不会去处理
// 交叉类型
interface A {
fn:(value: number)=> string
}
interface B {
fn:(value: string)=> string
}
type C = A & B
// 可以 相当于
fn:(value: string | number) => string
泛型
泛型是可以在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中。
需求:创建一个id函数,传入什么数据就返回该数据本身(也就是说,参数和返回值类型相同)
function id(value: number):number {return value}
// 比如 id(10) 调用以上函数就会直接返回10本身,但是该函数只接受数值类型,无法用于其他类型。为了让函数能够接受任意类型,可以将参数类型改为any
function id(value: any):any {return value} // 但是这样就失去了ts的类型保护
// 泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等于多种不同类型一起工作,灵活可复用
// 创建泛型函数
function id<Type>(value: Type):Type {return value}
// 调用
let num = id<number>(10) // num = 10
let str = id<string>('a') // str = 'a'
let num = id(100) // 也是可以的 类型可以省略
// 类型变量Type 是一种特殊类型的变量,他处理类型而不是值,相当于一个类型容器,捕获用户提供的类型,因为Type是类型,因此可以将其作为函数参数和返回值类型, Type可以是任意合法的变量名称
泛型约束
默认情况下,泛型函数的类型变量Type可以代表多个类型,这导致无法访问任何属性。 比如id(‘a’)调用函数时获取参数的长度
两种方式:1 指定更具体的类型,2 添加约束
function id<Type>(value: Type): Type{
log(value.length) // 报错
return value
}
// 因为Type可以代表任意类型,无法保证一定存在length属性,比如数字就没有长度,此时就需要为泛型添加约束。
指定更加具体的类型
// 指定更加具体的类型 [] 只要是数组就一定含有length属性
function id<Type>(value: Type[]): Type[] {
console.log(value.length) // 可以
return value
}
id([1,2,3])
通过extends为泛型添加约束
// 添加约束,通过extends 为泛型添加约束
interface Length {length:number}
function id<Type extends Length>(value: Type): Type {
console.log(value.length)
return value
}
id('123') // 可以
id([1,2,3]) // 可以
id({length:10}) // 可以 只要有length属性就行
多个泛型类型变量之间的约束 keyof
function id<Type, Key extends keyof Type>(obj:Type, key:Key) {
return obj[key]
}
let per = {name:'abc',age:10}
id(per,'name')
// keyof 关键字接受一个对象类型,生成其键名称的联合类型
// 示例中 keyof Type 实际上获取的是per对象里所有key的联合类型,也就是 'name'|'age'
// 类型变量Key受Type约束,可以理解为Key只能是Type所有键中的任意一个,或者说只能访问对象中存在的属性名称
泛型接口
接口也是可以配合泛型来使用,增加灵活性和复用性
interface func<Type> {
id: (value: Type) => Type,
ids:()=> Type[],
}
let obj: func<string> = {
id(value) {
return value
},
ids() {
return ['1']
}
}
console.log(obj.id('123'));
console.log(obj.ids());
// 在接口名称后面添加<类型变量>,那么这个接口就成了泛型接口。 接口中所有成员都可以使用类型变量、 使用泛型接口时需要指定具体的类型(比如此时的 func<string>)
泛型类
class类也可以配合泛型来使用
class GenericNumber<Type> {
value: Type
add: (x:Type,y:Type)=> Type
}
const my = new GenericNumber<number>()
my.value = 10
// 在class名称后面添加<类型变量>,这个类就变成了泛型类。
泛型工具类型
Ts里内置了一些常用的工具类型,来简化一些ts中的操作
都是基于泛型实现的
Partial
Partial用来构造一个类型,将Type的所有属性设置为可选
interface Props {
id: string
children: number[]
}
type PartialProps = Partial<Props>
// 构造出来的新类型PartialProps 结构和 Props相同,并且所有属性都是可选的
let props:PartialProps = {id:'1',children:[]}
props.id='2'
Readonly
Readonly用来构造一个类型,将Type的所有属性设置为readonly只读
interface Props {
id: string
children: number[]
}
type ReadonlyProps = Readonly<Props>
// 构造出来的新类型PartialProps 结构和 Props相同,但所有属性都是只读的
let props:ReadonlyProps = {id:'1',children:[]}
props.id='2' // 报错 因为是只读的
Pick<Type , Keys>
Pick<Type , Keys> 从Type中选择一组属性来构造新类型
interface Props {
id: string
title: string
children: number[]
}
type PickProps = Pick<Props, 'id' | 'title'>
// Pick有两个类型变量,1表示选择谁的属性,2表示选择哪几个属性
// 此处构造出来的PickProps就只有 id和title 两个属性
let props:PickProps = {id:'1',title:'a'}
// console.log(props); { id: '1', title: 'a' }
Record<Keys,Type>
Record<Keys,Type> 构造一个对象类型,属性键为keys,属性类型为Type
type RecordObj = Record<'a' | 'b' | 'c', string[]>
let obj: RecordObj = {
a: ['a'],
b: ['b'],
c: ['c']
}
// Record工具类型有两个类型变量,1表示对象里有哪些属性,2表示对象里的属性类型
索引签名类型 []
当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性)此时 就用到索引签名类型了
interface AnyObj {
[key:string]: any
}
let obj:AnyObj = {
a:'1',
b:2
}
// 使用[key:string]来约束该接口中允许出现的属性名称,表示只要是string类型的属性名称 都可以出现在对象中
// 这样对象obj中就可以出现任意多个属性
// key只是一个占位符,可以替换成任意合法的变量名称
// 注意:js对象中的key是string类型的
映射类型
基于一个旧的类型创建新的类型(对象类型),减少重复,提升效率
两种情况 1可以根据联合类型创建,2 可以根据对象类型创建
[key in keys] 根据联合类型创建
// 比如类型keys和types都有相同的 x y z
type keys = 'x' | 'y' | 'z'
type types = { x: number, y: number, z: number }
// 这样书写没错但x y z重复写了两次,可以用映射类型来简化
type keys = 'x' | 'y' | 'z'
type types = { [key in keys]: number }
// 映射类型是基于索引签名类型的,所以该语法类似于索引签名类型,也使用了[]
// key in keys 表示key可以是keys联合类型中的任意一个
// 映射类型只能在类型别名中使用,不能在接口中使用
[key in keyof keys] 根据对象类型创建
type keys = { x: number, y: number, z: number }
type types = { [key in keyof keys]: number }
let obj: types = {
x: 1,
y: 2,
z: 3
}
索引查询类型
用于查询属性的类型
可同时查询多个
// 查询一个
type keys = { x: number, y: string, z: boolean }
type typeA = keys['x']
let A: typeA = 1
let A: typeA = '1' // 报错
//keys['a']表示查询类型keys中属性a的类型 , 所以typeA的类型是number
// 查询多个
type keys = { x: number, y: string, z: boolean }
// 两种方式
type typeA = keys['x'|'y'] // number | string
type typeA = keys[keyof keys] // number | string | boolean
类型声明文件
用来为已存在的JS库提供类型信息, 这样在TS项目中使用这些库时,就像TS一样 都会有代码提示、类型保护等机制了
TS文件类型
TS 中有两种文件类型: 1:.ts文件 2:.d.ts文件
.ts文件
// 既包含类型信息又可执行代码
// 可以被编译成.js文件,然后执行代码
// 用途:编写程序代码的地方
.d.ts文件
// 只包含类型信息的类型声明文件
// 不会生成.js文件,仅用于提供类型信息
// 用途:为js提供类型信息
类型声明文件的使用
内置类型声明文件,第三方库的声明文件
创建自己的类型声明文件
- 创建index.d.ts 类型声明文件
- 创建需要共享的类型,使用export导出
- 在需要使用共享类型的.ts文件中 通过import导入即可(.d.ts后缀省略)
.d.ts
type keys = { x: number, y: number }
export {keys} // 导出
使用
import { keys } from '@/utils/index'
let a:keys ={
x:1,
y:2
}
VUE3中使用TS
- vscode装插件 : TypeScript Vue Plugin (Volar)
TS语法
可用TS语法来使用VUE3中的API
defineProps
子接收父传过来的值
<script setup lang='ts'>
// 定义别名
type PropsType = {
数据名?:string // 数据名?:数据类型 加上问号父不传值也不会报错
}
// 通过别名接收值
defineProps<PropsType>()
</script>
defineEmits
子传父 TS语法来约束传值类型
<script setup lang='ts'>
// TS语法
const emits = defineEmits<{
// 完整语法 (e:事件名,参数名1:参数类型,参数名2:参数类型):void
(e:'自定义事件名',value:string):void
}>()
// 子传父触发事件
const onClick = () => {
emits('自定义事件名', 'a')
}
</script>
ref
约束变量类型
<script setup lang='ts'>
import { ref } from 'vue'
// 定义一个泛型 {}[] 表示对象数组
type TodoList = {
id:number,
name:string,
done:boolean
}[]
const list = ref<TodoList>([
{id:1,name:'吃饭',done:true},
{id:2,name:'睡觉',done:false}
])
// 如果声明的数组不符合约束的类型会报错
</script>
x:1,
y:2
}
# VUE3中使用TS
1. vscode装插件 : TypeScript Vue Plugin (Volar)
### TS语法
> 可用TS语法来使用VUE3中的API
##### defineProps
> 子接收父传过来的值
```vue
<script setup lang='ts'>
// 定义别名
type PropsType = {
数据名?:string // 数据名?:数据类型 加上问号父不传值也不会报错
}
// 通过别名接收值
defineProps<PropsType>()
</script>
defineEmits
子传父 TS语法来约束传值类型
<script setup lang='ts'>
// TS语法
const emits = defineEmits<{
// 完整语法 (e:事件名,参数名1:参数类型,参数名2:参数类型):void
(e:'自定义事件名',value:string):void
}>()
// 子传父触发事件
const onClick = () => {
emits('自定义事件名', 'a')
}
</script>
ref
约束变量类型
<script setup lang='ts'>
import { ref } from 'vue'
// 定义一个泛型 {}[] 表示对象数组
type TodoList = {
id:number,
name:string,
done:boolean
}[]
const list = ref<TodoList>([
{id:1,name:'吃饭',done:true},
{id:2,name:'睡觉',done:false}
])
// 如果声明的数组不符合约束的类型会报错
</script>