Typescript基础巩固(持续更新)

开始

// 安装全局编译工具包
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可以代表多个类型,这就导致无法访问任何属性
解决:为泛型添加约束来收缩类型(缩窄类型的取值范围)

添加泛型约束收缩类型

  1. 指定更加具体的类型
  2. 添加约束
// 默认情况下,泛型函数的类型变量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 类型守卫函数

  1. 类型守卫函数在编程中通常用于在特定条件下确定变量的类型。
  2. 在ts中,可以提供更精确的类型信息。

用来缩小类型范围

  1. 使用现有的 JavaScript 构造来处理缩小范围 ,有两种主要形式的类型守卫函数:typeof / instanceof
  2. 使用类型谓词 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. 内置类型声明文件
  2. 第三方库类型声明文件(存在形式: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'
})

自定义类型声明文件–共享数据

  1. 创建.d.ts文件,并使用export导出
// 按需导出,提高性能
export type personType = {
    name: string
    age: number
}
  1. 使用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)
}

报错集合

  1. 无法在“–isolatedModules”下编译“main.ts”,因为它视为全局脚本…
    不建议,按要求export
// 在tsconfig.json中的isolatedModules修改为false
{
  "compilerOptions": {
    "isolatedModules": false,
    }
  }
}
  1. 找不到模块“./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"],
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值