开始
// 安装全局编译工具包
npm i -g typescript
// yarn global add typescript
// 查看版本
tsc -v
// 编译
tsc ".ts文件路径"
语法
自动类型推论,枚举,数组类型
// 自动类型推论
let age = 123 // let age:number = 123
// age = "122"
age = 234
// string 基本类型
let str:string = "ts"
console.log(str.length);
// vue 父子组件传参
/*
props: {
msg: {
type: String // 包装类
}
}
*/
// const 声明的变量是不可修改的,意味着从始至终都必须是3.14,所以ts将其当做一个类型来看,这种类型称为【字面量类型】
// 鼠标悬浮查看显示类型推断
let num1 = 3.14 // let num1: number
const num = 3.14 // const num: 3.14
// 字面量(固定的值:常量) : 10 20 "aa" [] {} /^1/
type position = 'top' | 'bottom' | 'left' | 'right'
function fn(value:position){
console.log(value);
}
// fn('test') 报错
fn('bottom')
// 枚举: 如果不设置值,默认从0开始
enum position {
top, // 0
bottom, // 1
left, // 2
right, // 3
}
const fn = (value:position):void => {
console.log(value);
}
fn(position.bottom)
// 设置值
enum position2 {
top = "上",
bottom = "下",
left = "左",
right = "右",
}
const fn2 = (value:position2):void => {
console.log(value);
}
fn2(position2.bottom)
// 数组类型
// 声明数组
// 方式1
let numArr: number[] = [1,2,3]
let strArr: string[] = ['1','2','3']
// 方式2
let numArr2: Array<number> = [6,8,9]
联合类型 | ,类型别名Type,可选链?,非空断言!
可选链操作符(判断属性是否存在),前面有(即不为null/undefined)才会执行后面的函数
非空断言:百分百确定有值的时候用,强制性
// 联合类型: |
let value:number | null = 1
value = null
let valueArr: (number | string)[] = [1,2,'3']
// |优先级较低,需要()包裹提升优先级
// 类型别名【复用性】
type valueType = (number | string)[]
let valueArr2:valueType = [1,2,'3']
let valueArr3:valueType = [1,2,'3']
// or
type valueType2 = number | string
let valueArr6:valueType2[] = [1,2,'3']
let valueArr7:valueType2[] = [1,2,'3']
// 其他用法
type s = string
let str:s = '123'
type objType = {
name: string
}
let obj:objType = {
name: '123'
}
type objType2 = {
name: string
age?: number,
gender?: string,
// fn: (value: string) => void
fn(value: string):void
}
let obj2:objType2 = {
name: '123',
fn(value){
console.log(value)
}
}
// 如报错:对象可能未定义
// obj2.name.concat("123")
// 传统
if(obj2.name){
obj2.name.concat("123")
}
// &&
obj2.name && obj2.name.concat("123")
// 可选链操作符(判断属性是否存在),前面有(即不为null/undefined)才会执行后面的函数
obj2.gender?.concat("性别")
// 非空断言:百分百确定有值的时候用,强制性
obj2.name!.length
// 对象函数调用
obj2.fn("hello world!")
函数类型
// 函数声明
// function 函数名(参数1:类型,参数2:类型):返回值类型{函数体}
function add(a:number,b:number):number{
return a+b
}
const result = add(1,2)
console.log(result);
// 函数表达式
const add2 = function(a:number,b:number):number{
return a+b
}
// 箭头函数
const add3 = (a:number,b:number):number => {
return a+b
}
// 函数的类型别名
// 类型别名通常是给箭头函数/函数表达式使用的,不会给函数声明使用
type addType = (a:number,b:number) => number
const add3Type: addType = (a,b) => {
return a+b
}
const add2Type: addType = function(a,b){
return a+b
}
// 如果不写return 默认返回 undefined,ts自动类型推断为void
// void 无返回值
const fn = (value:string):void => {
console.log(value);
}
fn("hello world!")
// 必选参数不能再可选参数后
const paramsFn = (name:string,age?:number):void => {
console.log(name,age);
}
paramsFn("xiao",18)
paramsFn("Li")
元祖、any
// 数组类型:可以限定类型,无法限制长度
let arr:number[] = [1,2,3]
// 元祖类型:限定数组的固定类型和数组长度
// 地图:经纬度
let arr2:[number,string] = [1,'2']
// any类型:不建议使用,会失去ts类型保护机制
let a:any = 1
a = '1'
never
缩小范围时,您可以将联合选项减少到已经消除所有可能性并且一无所有。在这些情况下,TypeScript 将使用类型 never 来表示不应该存在的状态。
正确示例
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
// never 来表示不应该存在的状态。
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
never 错误示例
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
// 向 Shape 联合添加新成员将导致 TypeScript 错误:
interface Triangle {
kind: "triangle";
sideLength: number;
}
type Shape = Circle | Square | Triangle;
// never 来表示不应该存在的状态。
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
default:
const _exhaustiveCheck: never = shape; // 不能将类型“Triangle”分配给类型“never”。
return _exhaustiveCheck;
}
}
泛型
泛型是可以保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用(常用语函数、接口、class中)
调用fn函数时来指定传入参数类型
/*
function fn(val: string | number | boolean){
return val
}
*/
// 泛型
// 期望:调用fn函数时来指定传入参数类型
// <T> : 声明泛型
// val: T : 使用泛型
function fn<T>(val: T){
return val
}
// console.log(fn<number>(12));
// console.log(fn<string>("hello world!"));
// 简化写法:调用时可以不加<类型> ts会自动推断类型
const result = fn(12)
console.log(result); // 12
console.log(fn("hello world!"));
result.toFixed(2)
泛型-类型约束
默认情况下,泛型函数的类型变量Type可以代表多个类型,这就导致无法访问任何属性
解决:为泛型添加约束来收缩类型(缩窄类型的取值范围)
添加泛型约束收缩类型
- 指定更加具体的类型
- 添加约束
// 默认情况下,泛型函数的类型变量Type可以代表多个类型,这就导致无法访问任何属性
function fn<T>(val: T){
// console.log(val.length); // 类型“T”上不存在属性“length”。
return val
}
const result = fn(12)
console.log(result);
console.log(fn("hello world!"));
result.toFixed(2)
// 1、指定更加具体的类型
// 指定泛型类型的数组,val确定是一个数组,可以使用length属性
function fnArr<T>(val: T[]){
console.log(val.length);
return val
}
fnArr([1,2])
fnArr(['1','2','hello'])
// 2、添加约束
// 创建一个接口,通过extends关键字为泛型添加约束
interface Ilength {
length: number
}
function fnLen<T extends Ilength>(val: T){
console.log(val.length);
return val
}
console.log(fnLen("hello world!"));
// console.log(fnLen(123)); // 类型“number”的参数不能赋给类型“Ilength”的参数。
// 多个泛型
// 返回值any类型,如何解决
function fnAll(obj: object,key: string){ // function fnAll(obj: object, key: string): any
return obj[key]
}
// function fnAll1<O,K>(obj: O,key: K){
// return obj[key] // 类型“K”无法用于索引类型“O”。(即:不知道obj对象中是否存储对应的属性)
// }
// fnAll1({name: 'Li'},'name')
// fnAll1({name: 'Li'},'age')
// fnAll1({name: 'Li',gender: '女'},'gender')
// 解决 keyof
// K extends keyof O -> 继承O所有key
function fnAll2<O,K extends keyof O>(obj: O,key: K){
return obj[key] // 类型“K”无法用于索引类型“O”。(即:不知道obj对象中是否存储对应的属性)
}
fnAll2({name: 'Li'},'name')
// fnAll1({name: 'Li'},'age') // 类型“"age"”的参数不能赋给类型“"name"”的参数
fnAll2({name: 'Li',gender: '女'},'gender')
const person = {
name: 'TT',
age: 19
}
fnAll2(person,'age') // 会自动提示可选key
keyof 常规用法
type T = {
name: string
age: number
gender?: number
}
let num: keyof T = 'age' // 即 let num:number
泛型接口
interface IPerson<T>{
name?: string
age: T
hobby?: T[]
}
let obj:IPerson<number> = {
age: 18
}
let obj2:IPerson<string> = {
age: '18',
hobby: ['1','3','2']
}
const arr1:number[] = [1,2]
// 泛型接口 interface Array<T>
const arr:Array<number> = [1,2]
Type Guards 类型守卫函数
- 类型守卫函数在编程中通常用于在特定条件下确定变量的类型。
- 在ts中,可以提供更精确的类型信息。
用来缩小类型范围
- 使用现有的 JavaScript 构造来处理缩小范围 ,有两种主要形式的类型守卫函数:typeof / instanceof
- 使用类型谓词 is
js运算符in
用于确定对象或其原型链是否具有具有名称的属性
type Fish = { swim: () => void };
type Bird = { fly: () => void };
type Human = { swim?: () => void; fly?: () => void };
// 缩小类型范围
function move(animal: Fish | Bird | Human) {
if ("swim" in animal) {
animal; // (parameter) animal: Fish | Human
} else {
animal; // (parameter) animal: Bird | Human
}
}
js运算符instanceof
用于检查一个值是否是另一个值的“实例”
function logValue(x: Date | string) {
if (x instanceof Date) {
console.log(x.toUTCString()); // (parameter) x: Date
} else {
console.log(x.toUpperCase()); // (parameter) x: string
}
}
类型谓词 is
注意:is不是ts语法中的关键字,而是一种惯例,通常用于命名用户自定义的类型守卫函数。
通常自定义类型守卫函数,可以在代码块中缩小变量的类型范围,以便更准确地推断变量的类型。
// 定义一个函数,其返回类型为类型谓词
type Fish = { swim: () => void };
type Bird = { fly: () => void };
// pet is Fish 是本例中的类型谓词。
// 谓词采用 parameterName is Type 的形式,其中 parameterName 必须是当前函数签名中的参数名称。
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
// --------------------------
// 自定义类型守卫函数
function isString(value:any):value is string{
// return '' // 不能将类型“string”分配给类型“boolean”
return typeof value === 'string'
}
let example:any = "hello"
let count:any = 1
if(isString(example)){
console.log("example类型被缩小为string");
}
if(isString(count)){
console.log("不是string类型");
}
dom元素,ts关键字as(类型断言)
as用于类型断言,即开发者明确告诉ts某个值的类型。
在你清楚知道某个值的类型并想要覆盖ts的类型检查时很有用。
// dom元素类型
// 技巧
// 1、获取dom元素的ts类型: 浏览器控制台查看[[Prototype]]
// 2、const result = document.createElement("a") // const result: HTMLAnchorElement
const result = document.createElement("a") // 鼠标悬浮result查看显示类型推断
// 获取a标签类型
const a = document.getElementById('getLink') // 自动类型推断 HTMLElement | null
// 获取div标签类型
const div = document.getElementById('getDiv')
// 报错 类型推断不正确
// a && a.href // 类型“HTMLElement”上不存在属性“href”。
// 解决:类型断言as【自定义获取指定元素的结果类型】
const aAs = document.getElementById('getLink') as HTMLAnchorElement
console.log(aAs.href);
// HTMLElement是父,HTMLAnchorElement是子,层级关系
interface vs type
// interface vs type
// interface只能约束对象,type可以更灵活使用
// 推荐type写法
/*
给对象约束属性和方法
基础语法
interface 接口名{
属性名: 类型
}
*/
interface Iobj {
name: string
age: number
gender?: number
fn: (value:string) => void
}
const obj:Iobj = {
name: "Li",
age: 18,
fn(value){
console.log(value)
}
}
obj.fn("hello")
// 接口继承【Ison具备Iobj的所有约束规则】
interface Ison extends Iobj {
score: number
eat():void
}
// type实现继承效果
type objType = {
name: string
age: number
gender?: number
fn: (value:string) => void
}
// 继承 &与连接符:既要满足前面的也要满足后面的
type sonType = {
score: number
eat():void
} & objType
const obj2: Ison = {
score: 100,
eat(){
console.log("eat!")
},
name: "Li",
age: 18,
fn(value){
console.log(value)
}
}
const obj3: sonType = {
score: 100,
eat(){
console.log("eat!")
},
name: "Li",
age: 18,
fn(value){
console.log(value)
}
}
// type |或连接符:实现其中一种约束即可
type orType = {
score: number
eat():void
} | objType
const obj6: orType = {
score: 100,
name: "Li",
age: 18,
fn(value){
console.log(value)
}
}
ts类型声明文件
ts中有两种文件类型
- .ts文件:包含类型信息以及执行代码,可被编译成.js文件,用于程序编写
- .d.ts文件:只包含类型信息的类型声明文件,不会生成.js文件,用于提供类型信息
项目开发中写的.ts文件,上线时会打包成.js文件,丢失了类型。解决:需要额外准备一个类型声明文件(.d.ts)
在项目开发中使用的第三方库,会有对应的ts类型,如何实现的呢?类型声明文件(.d.ts)
如何为老项目的js库提供类型信息,使用.d.ts文件(类型声明文件)
类型声明文件
- 内置类型声明文件
- 第三方库类型声明文件(存在形式:1、库自带类型声明文件[axios];2、由definitelyType(github仓库)开源组织提供的,一般通过安装对应依赖npm i @type/xxx -D[jquery没有自带的类型声明文件,安装npm i @type/jquery -D])
// npm i axios
// 库自带类型声明文件
// 接口返回值类型
type ResType = {
data: {
list: {
id: number
name: string
address: string
}
}
message: string
code: number
}
import axios from 'axios'
async function getApi(){
const res = await axios.get<ResType>("api地址") // const res: AxiosResponse<any, any>
console.log(res.data.code); // res.data之后就没有提示了,需要自定定义接口返回的属性类型
}
/**
* 无法找到模块“jquery”的声明文件。“d:/study/vue3-ts/node_modules/jquery/dist/jquery.js”隐式拥有 "any" 类型。
* 尝试使用 `npm i --save-dev @types/jquery` (如果存在),或者添加一个包含 `declare module 'jquery';` 的新声明(.d.ts)文件
*/
import $ from 'jquery'
// 没有提示
// 安装依赖 npm i @types/jquery -D
$.ajax({
method:'GET'
})
自定义类型声明文件–共享数据
- 创建.d.ts文件,并使用export导出
// 按需导出,提高性能
export type personType = {
name: string
age: number
}
- 使用Import导入(注意:.d.ts文件后缀不需要填写)
import type { personType } from './types/index'
let p: personType = {
name: 'Zz',
age: 18
}
为老项目添加类型声明文件
注意 .js和.d.ts文件名必须一致
home.js
// js文件,老项目
let count = 10
let str = "hello world"
let position = {
top: 0,
bottom: 10
}
function fn(val){
console.log(val);
}
function fnArrow(a,b){
return a+b
}
export {
count,
str,
position,
fn,
fnArrow
}
home.d.ts
// declare let count:number // 文件“d:/study/vue3-ts/src/types/index.d.ts”不是模块。
export declare let count:number
export declare let str:string
export declare let position: {top: number,bottom: number}
enum position {
top = "上",
bottom = "下",
left = "左",
right = "右",
}
export declare function fn(params:position) :void
// export declare const fnArrow = (val:number) => number // 报错
type fnType = (a:number,b:number) => number
export declare const fnArrow:fnType
使用
index.ts
// 报错 文件“d:/study/vue3-ts/src/types/index.d.ts”不是模块。
// 解决: 创建home.d.ts类型声明文件
import { count,fnArrow,fn, position } from './home'
fn(position.bottom)
const result = fnArrow(count,20)
console.log(result )
vue
vscode
Vue Language Features (Volar)
TypeScript Vue Plugin (Volar):用于支持在 TS 中 import *.vue 文件。
Error Lens
vuejs官网组合 API 的 TypeScript
快速创建项目模版:vabtss or vab-ts-setup
建议根据官方文档学习,其他文档与官网内容会有出入。
基础
<template>
<div class="-container">
<div>使用ref: {{ obj.name }}</div>
<div>使用reactive: {{ obj2.name }}</div>
<div>使用computed: {{ computedLength }}</div>
<button @click="btnClick">点击事件</button>
<div>
<img
ref="myImg"
src="https://copyright.bdstatic.com/vcg/creative/cc9c744cf9f7c864889c563cbdeddce6.jpg@h_1280"
width="100"
height="100"
/>
</div>
<div>
<button @click="getImgRef">修改图片-null</button>
</div>
</div>
</template>
import { computed, reactive, ref } from 'vue'
defineOptions({
name: 'BaseGrammar'
})
// --------------ref--------------------
// 为ref设置泛型
const str = ref<string>('hello world')
// const obj = ref<{
// name: string,
// age?:number,
// bol: boolean
// }[]>([
// {name: 'Li',bol:false},
// {name: 'Z',bol:true,age: 18}
// ])
type Person = {
name: string
age?: number
bol: boolean
}
const list = ref<Person[]>([
{ name: 'Li', bol: false },
{ name: 'Z', bol: true, age: 18 }
])
let obj: Person = list.value[0]
obj.name = 'Tt'
console.log(obj.name)
// --------------reactive--------------------
// 建议使用ref定义变量,reactive更新数据时需要注意丢失响应式问题
const obj2 = reactive<Person>({ name: 'Li', bol: false })
// 直接修改
obj2.name = 'Zz' // 没有丢失
// --------------computed--------------------
// 计算属性:不需要特意指定类型,会自动类型推断
const computedLength = computed<number>(() => {
return list.value.length
})
console.log(computedLength.value)
// --------------点击事件--------------------
// e默认参数,推断类型技巧
// 第二种在点击事件(写入$event),鼠标悬浮$event查看推断类型 <button @click="btnClick($event)">点击事件</button>
function btnClick(e: MouseEvent) {
// 第一种:打印控制台,查看[[Prototype]]
console.log(e) // PointerEvent -> MouseEvent
console.log(e.pageX, e.pageY)
}
// --------------Dom Ref--------------------
// 查看类型
const img = document.createElement('img')
// 类型“never”上不存在属性src 解决:指定具体类型:HTMLImageElement
const myImg = ref<HTMLImageElement | null>(null) // 获取实例
console.log(myImg.value) // null
const getImgRef = () => {
console.dir(myImg.value)
if (myImg.value) {
myImg.value.src = ''
}
// console.log(myImg.value && myImg.value.src);
console.log('获取地址', myImg.value?.src) // “myImg.value”可能为 “null”。解决:可选链?
// 非空断言(慎用):百分百确定的值才能使用
console.log('获取地址', myImg.value!.src) // 解决:非空断言!
}
父子组件
1、可以使用基于类型的声明或运行时声明,但不能同时使用两者。
// 非ts语法
// 运行时声明
// const props = defineProps({
// obj: { type: String, required: true, default: '默认值' }, // 无提示属性,自己打全
// msg: String
// })
// props.msg
// ts语法
// 基于类型的声明: 通过泛型类型参数定义具有纯类型的 props
type PropsType = {
msg: string // 必传
age?: number // 可选
hobby?: string[]
}
// const props = defineProps<PropsType>()
// 注意解构不是响应式
// const { msg } = defineProps<PropsType>()
// 设置默认值
// const { msg = "默认值" } = defineProps<PropsType>()
// 当使用基于类型的声明时,我们失去了为 props 声明默认值的能力。这可以通过 withDefaults 编译器宏解决
const props = withDefaults(defineProps<PropsType>(), {
msg: 'hello',
age: 18,
hobby: () => ['one', 'two']
})
// --------------------------------
// 非ts语法
// const emit = defineEmits(['btnChange', 'sendMed'])
// ts语法
// 对事件名和参数做约束
// 基于选项
// const emit = defineEmits({
// sendMed: (val: number) => {
// // 返回 `true` 或 `false`
// // 表明验证通过或失败
// return true
// },
// btnChange: (val: string) => {
// // 返回 `true` 或 `false`
// // 表明验证通过或失败
// return true
// }
// })
// 基于类型
// 语法:(e:事件名,参数1:类型,参数2:类型):void
// const emit = defineEmits<{
// (e: 'btnChange',val: string):void
// (e: 'sendMed',val: number):void
// }>()
// 3.3+: 可选的、更简洁的语法
const emit = defineEmits<{
sendMed: [val: number]
btnChange: [val: string]
}>()
const btnChange = () => {
emit('btnChange', '子组件点击事件')
}
const sendMed = () => {
emit('sendMed', 1)
}
报错集合
- 无法在“–isolatedModules”下编译“main.ts”,因为它视为全局脚本…
不建议,按要求export
// 在tsconfig.json中的isolatedModules修改为false
{
"compilerOptions": {
"isolatedModules": false,
}
}
}
- 找不到模块“./xxx.vue”或其相应的类型声明。
尝试: Vue Language Features (Volar)重启,或者vscode重新打开
【不好,存在any】
// 解决 配置 让ts认识.vue后缀的文件 默认没有 【有疑问】
// env.d.ts 声明文件扩充
declare module '*.vue' {
import type { DefineComponent } from "vue"
const component:DefineComponent<{},{},any>
export default component
}
// 在tsconfig.json compilerOptions属性添加
"baseUrl": "./",// 解析非相对模块的基础地址,默认是当前目录
"paths": {
// 路径映射,相对于baseUrl
"@/*":["src/*"]
}
// 在tsconfig.json include属性引入 'env.d.ts'
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue","env.d.ts"],