ts是js的超级,提供了一种静态语言的类型系统
npm install -g typescript
tsc -v查看版本
写完代码后使用tsc来编译成js
如果报“tsc : 无法加载文件 C:\Users\Wei.Du7\AppData\Roaming\npm\tsc.ps1,因为在此系统上禁止运行脚本”
以管理员运行powershell

set-ExecutionPolicy RemoteSigned

基础

类型

需要记住的是,ts里面冒号后面都是在申明类型,与实际功能无关

let isDone: boolean = false
let age: number = 10
let firstName: string = 'zhangsan'
let message: string = `hello ${firstName}`

let u: undefined = undefined
let n: null = null

let notSure: any = 4 //不定类型
notSure = 'is string'
notSure.myName

let arrOfNumber: number[] = [1,2,3]//数组类型
arrOfNumber.push(4) //数组添加元素
let user: [string, number] = ['zhangsan',114514]//元组

let numberorstring: number|string//union types

//枚举
enum Direction { //枚举,如果不赋值初始第一项默认为0,值依次增大.除了数字页可赋值为其他
    Up = 'UP',
    Down = 'Down',
    Left = 'LEFT',
    Right = 'RIGHT',
}
console.log(Direction.Up)
console.log(Direction[0])

接口

interface Person {
    name: string;
    age?: number; //?:表示非必选
    readonly id: number //readonly表示只读,创建后不能被修改
}

let visiter: Person = {
    name: 'zhangsan',
    age: 20,
    id: 114514
}

函数

function add(x: number, y: number, z?: number): number {
    if (typeof z === 'number') {
        return x+y+z
    }
    return x+y
}
const addtw = (x: number, y: number, z?: number): number => {
    if (typeof z === 'number') {
        return x+y+z
    }
    return x+y
}//类型推论,推为函数
interface ISum {
    (x: number, y: number, z?: number): number 
}
let addtr: ISum = add

联合类型与类型推论、类型守卫
联合类型默认只能使用多种类型共有的方法,当需要使用某种类型独有的方法时,需要使用类型断言或者类型守卫来处理

let numberorstring: number|string//union types,只能访问共有的方法
function getLength(input: number|string): number{
    const str = input as string //类型断言
    if (str.length){
        return str.length
    }else{
        const number = input as number
        return number.toString().length
    }
}
function getLength2(input: number|string): number{ //类型守卫
    if (typeof input === 'string'){
        return input.length
    }else{
        return input.toString().length
    }
}

泛型,可以看作占位符,在使用的时候才去动态的确定值
为什么要用泛型而不是any?这就涉及到一个概念叫做鸭子类型,只要他拥有某个属性,那么不管他是什么类型的,他都满足我们所定义的泛型,也就是只要看起来像鸭子,叫起来也像鸭子,那管他是不是鸭子,都能当作鸭子

function echo<T>(arg: T): T {//泛型,不指定具体的值的类型,T只是一个约定俗成的记号
    return arg
}
const result = echo(true)//类型推断为true
function swap<T,U>(tuple: [T,U]):[U,T] {
    return [tuple[1],tuple[0]]
}
const result2 = swap(['string',123])//类型推断为 result2: [number, string]

//鸭子类型示意
interface ItemwithLength { //创建一个接口,拥有number类型的length属性
    length: number
}
//T类型,但是要求必须拥有满足ItemwithLength的方法/属性
function echoLength<T extends ItemwithLength>(arg: T): T{
    console.log(arg.length)
    return arg
}
//可以看到凡是有number类型的length的都可以被当作参数
const str = echoLength('str')
const obj = echoLength({length: 123,height: 114})
const arr = echoLength([1,2,3])

//可以接纳多种类型元素的数组
class Queue<T> {
    private data:Array<T> = [];
    
    push(item: T) {
        return this.data.push(item);
    }
    
    pop(): T | undefined{
        return this.data.shift();
    }
}

const queue = new Queue<number>()
queue.push(1)
const rst = queue.pop();
if (rst !== undefined && rst !== null) {
    console.log(rst.toFixed());
}

//泛型用于接口
interface KeyP<T, U>{
    key: T
    value: U
}
let key1: KeyP<string,number> = {key:'marisa', value:1}
let key2: KeyP<number,string> = {key:2, value:'reimu'}

类型别名

type SumType = (x: number, y: number) => number
let sum2: SumType = (x, y) => {
    return x + y;
};
const result3 = sum2(2,3)

type Directions = 'Up' | 'Down' | 'Left' | 'Right'
let toWhere: Directions = 'Left'

//使用别名进行结构拼接
interface IName {
    name: string
}
type IPerson = IName & {age: number}
let person: IPerson = { name: 'lilis', age: 13}

声明文件

ts使用外来的js需要声明文件,格式为.d.ts,一般知名库都有现成的声明文件,安装后就可以直接import导入,比如axios,所以只需要直接npm安装对应的库就行了

内置

标准的ECAM和dom语法都适用

const date = new Date()
date.getTime()
const reg = /abc/
reg.test('abc')

Math.pow(2,2)

let body = document.body

document.addEventListener('click',(e) => { //这里类型推断e为MouseEvent
    e.preventDefault()
})

ts自身也拥有一些内置的utility type,比如Partial,可以把接口里面的属性都变成可选的

interface Exampletype{
    name: string
    no: number
}
type Exampletypechoseable = Partial<Exampletype>
//使用后的类型变成:
// type Exampletypechoseable = {
//     name?: string | undefined;
//     no?: number | undefined;
// }

配置文件

tsconfig.json

{
    //"files": [],//tsc默认只会编译nodemodule下的声明文件,如果有自己写的要放在这
    //"include": []
    //"exclude": []
    "compilerOptions": {
        "outDir": "./output",//编译输出目录
        "module": "ESNext",//输出的模块类型
        "target": "ES6",//输出的版本
        "declaration": true,//给每个js文件都输出.d.ts
    }
}

export

示例

import request from "./requestconfig"


function getMainMenu(){
    return request({
        method: 'post',
        url:"/menu/goods/",
    })
}


function getSecondMenu(mainMenuId: string | number): Promise<any> {
    return request({
        url:"/menu/submenu?mainMenuId="+mainMenuId,
    });
}

export { getMainMenu, getSecondMenu };
//有多个的时候可以这么写,如果只有单个可以写export default xxx,但是注意,如果用了default却导出多个,那么导出的都会变成第一个的功能

使用

模块化示例

作为模块放进vue使用
示例
src/hooks/useMousePosition.ts

import {ref, onMounted, onUnmounted} from 'vue' 
function useMousepostion(){
    const x = ref(0)
    const y = ref(0)
    function updateMouse(e: MouseEvent){
    x.value = e.pageX
    y.value = e.pageY
    }
    onMounted(()=>{
    document.addEventListener('click',updateMouse)
    })
    onUnmounted(()=>{
    document.removeEventListener('click',updateMouse)
    })
    return {x,y}
}

export default useMousepostion

app.vue中使用

import useMousepostion from './hooks/useMousePosition'
const {x, y} = useMousepostion()

jsconfig

{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": [    "env.d.ts",
  "src/**/*",
  "src/**/*.vue",
  "src/**/*.ts"],
  "exclude": ["src/**/__tests__/*"],
  "compilerOptions": {
    "composite": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    "declaration": true
  }
}

泛型结合模块

import { ref } from 'vue'
import axios from 'axios'

function useURLLoader<T>(url: string){
    const result = ref<T | null>(null)
    const loading = ref(true)
    const loaded = ref(false)
    const error = ref(null)

    axios.get(url).then((rawData) => {
        loading.value = false
        loaded.value = true
        result.value = rawData.data
    }).catch(e => {
        error.value = e
        loading.value = false
    })
    return {
        result,
        loading,
        loaded,
        error
    }
}

export default useURLLoader
interface Animresult{
  code: string
  data: string
}
watch(result,()=>{
  console.log(result.value?.data)
})
const {result,loading,loaded} = useURLLoader<Animresult>("https://mock.apifox.cn/m1/3372030-0-default/pet/1")
      <h1 v-if="loading">Loading</h1>
      <p v-if="loaded">reslut:{{ result?.data }}</p>

api数据结构

{
    "code": 0,
    "data": {
        "name": "Hello Kity",
        "photoUrls": [
            "http://dummyimage.com/400x400"
        ],
        "id": 3,
        "category": {
            "id": 71,
            "name": "Cat"
        },
        "tags": [
            {
                "id": 22,
                "name": "Cat"
            }
        ],
        "status": "sold"
    }
}

如果后面想接类似逻辑,但是数据结构不一样的api,只需要传入不同的interface就可以了,不需要重复造轮子

传送、悬浮框、emit和props

搞一个居中的悬浮窗,要求能够用按钮来控制开关,并且为了防止嵌套过深,把他传送挂载到和app平级的一个div下
模组

<template>
    <Teleport to="#modal">
        <div id="centermodal" v-if="isOpen">
            <h2><slot>Modal window</slot></h2>
            <button @click="$emit('onModalclose')">close</button>
        </div>
    </Teleport>
</template>

<script setup lang="ts">
defineProps({
  isOpen: Boolean
});
defineEmits(['onModalclose'])
</script>

<style>
  #centermodal {
    width: 200px;
    height: 200px;
    border: 2px solid black;
    background: white;
    position: fixed;
    left: 50%;
    top: 50%;
    margin-left: -100px;
    margin-top: -100px;
  }
</style>

app.vue

import ModalControl from './components/ModalControl.vue'
const modalIsOpen = ref(false)
function openModal(){
  modalIsOpen.value = true
}
function onModalclose(){
  modalIsOpen.value = false
}

      <button @click="openModal">Open Modal</button>
      <ModalControl :is-open="modalIsOpen" @on-modalclose="onModalclose">My Modal !!!</ModalControl>

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vite App</title>
  </head>
  <body>
    <div id="app"></div>
    <div id="modal"></div>//传送到这
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

全局provide

在main.js中挂载provide
app.provide('username',{name: 'marisa'})
在某个组件中使用

<template>
    <h1>{{ result }}</h1>
    <h1>{{ location }}</h1>
    <h1>{{ username && username.name }}</h1>
    <span></span>
</template>

<script setup lang="ts">
import {ref, inject} from 'vue'
const location = inject('location')
const username = inject<{name: string}>('username')
//这里使用了泛型来告诉username是个什么类型的数据,不然js不知道username有什么属性

const result = ref<number | null>(null);

new Promise<number>((resolve) => {
  setTimeout(() => {
    resolve(42);
  }, 3000);
}).then((value) => {
  result.value = value;
});
</script>

标签: none

添加新评论