Typescript
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>
评论已关闭