基础类型
数字类型
let num1:number = 100;
let num2:number = 0b100; // 2进制
let num3:number = 0o100; // 8进制
let num4:number = 0x100; // 16进制
let num5:number = 100.88; // 小数也是数字类型
// 类型定义好以后,不可以错误赋值
// num5 = 'abc'
console.log(num1); // 100
console.log(num2); // 4
console.log(num3); // 64
console.log(num4); // 256
console.log(num5); // 100.88
boolean类型
let flag:boolean = true;
flag = false;
flag = 20 >5;
flag = !!100;
console.log(flag); // true
string类型
let one:string = 'www'
let dot:string = '.'
let two:string = 'baidu'
let three:string = `
https//${one}${dot}${two}.com
`
console.log(three) // https//www.baidu.com
数组
let arr:string[] = []; // 声明字符串数组,不可以放其他类型
arr.push('123');
arr.push('abc');
// arr.push(123); // 放数字类型会报错
console.log(arr); // [ '123', 'abc' ]
其他特殊类型
let a: null = null // 声明了null类型,就只能赋值null,表示对象确实
let b: undefined = undefined // 声明了undefined类型,就只能赋值undefined,用于初始化变量是未定义的值
let c: any // any表示任意类型,可以忽略类型限制,可以js一样了
let d // 声明变量不指定类型,默认为any类型
// 可以赋值任意类型的值
c = 10
c = 'abc'
c = true
let e: unknown // unknown表示未知类型,还不知道什么类型,但是安全的,具体意思看下面
e = 'www'
e = 100
let f: string
// e = f; // 指定类型的变量不可以赋值给unknown类型
// unknown类型的可以赋值给unknown类型 和 any类型
let g: unknown
g = e
let h: any
h = e
e = h // any类型也可以赋值给unknown
// never 表示接口永远没有返回值
function fun(): never {
while (true) {
// ...接口一直执行循环,永远不会返回
// 或者抛异常
throw new Error('error')
}
}
对象类型
// 推断的方式直接赋值了
let user = {
name: '张三',
age: 30
}
console.log(user.name); // 张三
// 第二种方式声明对象类型限制
let userInfo: { name: string } // 先声明
userInfo = { name: '张三' } // 再赋值
// let userInfo: { name: string } = { name: '张三' } // 也可以边声明边赋值
console.log(userInfo.name) // 张三
// 当不知道对象后面有多少属性,也不知道都有哪些类型,这样写
let obj: { name: string; age: number; [propName: string]: any } // 添加了数组表示多个属性,不指定类型
obj = { name: 'abc', age: 12, gender: 'male', email: '[email protected]' }
运算符号
// | 符合等于拼接
let info: string | number // info可以是string和number两种数据类型
info = 'abc'
info = 123
// info = true // 报错,因为info只能是string和number两种数据类型
let arr: (string | number | boolean)[] = ['a', 1, true]
arr.push('b')
arr.push(2)
// & 符号表示与关系,如下表示对象使用时必须要有name和age两个属性都赋值才行
let user: { name: string } & { age: number }
关键字
tuple 元组
// 声明数组
let array1: string[] = ['a', 'b', 'c']
// 声明只允许两种类型值的数组
let array2: (string | number)[] = ['a', 'b', 'c', 1, 2, 3]
// 指定数组长度的数组
let array3: [string, string, string, number] = ['a', 'b', 'c', 1]
enum 枚举
enum sex {
Famale = '男',
male = '女'
}
console.log(sex.Famale) // 男
type 别名
type mystate = 1 | 2 | 3 | 4 | 5
let state: mystate
state = 1
// state = 7 // 只能使用12345这5个数,所以赋值7会报错
// 示例
// 声明了别名
type myFun = (a: number, b: number) => number
// 使用别名,只能是这个别名的类型,
let testTypeFun: myFun = (a: number, b: number) => a * b
console.log(testTypeFun(2, 3)) // 6
类型断言
as
function demo01(n: number | string) {
let l: number
// n参数是字符串,就把长度赋值给l
l = (n as string).length
console.log(l) // undefined
}
demo01(1)
// 解构断言
function funTest() {
let str: string = 'www.baidu.com '
let fun = (a: number, b: number) => a + b
return [str, fun] // 返回2个值
}
// 断言这个函数返回类型
let [a, b] = funTest() as [string, Function]
console.log(b(1, 2)) // 3
as const
let arr01 = ['www', 123] as const
// arr01.push(456); // 指定了为常量,不可以修改值
console.log(arr01)
let user = {
name: '张三',
age: 30
} as const
// user.name = '李四' // 不可以修改常量的值
函数类型
// 声明函数,c是可选参数
function func01(a: number, b: number, c?: number): number {
return a + b
}
console.log(func01(1, 2))
// 箭头函数声明,后面跟上返回值类型number
let func02: (a: number, b: number) => number
// 赋值
func02 = (a, b) => a * b
console.log(func02(1, 2))
// 声明别名
type myFun = (n: number, m: number) => number
// 声明函数,两个参数,第3个参数是回调函数,返回使用的,使用的是myFun类型
function calc(num1: number, num2: number, callback: myFun) {
return callback(num1, num2)
}
// 使用函数
console.log(calc(1, 2, (n, m) => n + m)) // 3
// 声明赋值函数,参数b选填参数
const funcTest = function (a: number, b?: number): number {
return 0
}
funcTest(1)
funcTest(1, 2)
funcTest(1, undefined)
// 参数b默认是2
const funcTest2 = function (a: number, b: number = 2) {
return a + b
}
function fun3(...args: any[]) {
console.log(args) // [ 1, 2, 3 ]
}
fun3(1, 2, 3)
// 函数重载写法,不写方法体
function addTest(a: number, b: number): number
function addTest(a: string, b: string): string
// 具体实现
function addTest(a: any, b: any): any {
return a + b
}
console.log(addTest(1, 2)) // 3
console.log(addTest('a', 'b')) // ab
类
定义类
class Person {
name: string
age: number
// 构造方法
constructor(name: string, age: number) {
this.name = name
this.age = age
}
// 类的属性
sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
}
}
const p = new Person('张三', 30)
p.sayHello() // 输出:Hello, my name is 张三 and I am 30 years old.
继承
class Person {
name: string
age: number
// 构造方法
constructor(name: string, age: number) {
this.name = name
this.age = age
}
// 类的属性
sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
}
}
// 声明Student类继承Person,子类只能继承 1个父类,父类可以有多个子类
class Student extends Person {
school: string
constructor(name: string, age: number, school: string) {
super(name, age) // 调用父类的constructor(name: string, age: number)
this.school = school
}
study() {
console.log(`${this.name} is studying at ${this.school}.`)
}
}
const s = new Student('张三', 30, '家里蹲')
s.sayHello() // 输出:Hello, my name is 张三 and I am 30 years old.
s.study() // 输出:张三 is studying at 家里蹲.
// 声明Teacher继承Student,Student继承了Person,所以Teacher也有Person属性,并且有Student属性
class Teacher extends Student {
price: number
constructor(name: string, age: number, school: string, price: number) {
super(name, age, school)
this.price = price
}
// 重写父类的study方法
study() {
console.log(`${this.name} is teaching at ${this.school} for ${this.price} dollars.`)
}
}
const t = new Teacher('李四', 40, '加拿大', 1000)
t.sayHello() // 输出:Hello, my name is 李四 and I am 40 years old.
t.study() // 输出:李四 is teaching at 加拿大 for 1000 dollars.
封装
修饰符
TypeScript可以使用三种访问修饰符,分别是public, private 和 protected
readonly可以修饰属性可读
class Person {
// 不写修饰符,默认是public
private name: string
readonly age: number
// 构造方法
constructor(name: string, age: number) {
this.name = name
this.age = age
}
// 类的属性
sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`)
}
}
const person = new Person('Alice', 25)
// console.log(person.name); // 不能访问属性,只能在类的内部
console.log(person.age) // 输出:25 能访问属性,因为age是只读的
// person.age = 18; // 无法修改属性,因为age是只读的
封装特性和存储器
class Person {
name: string
private _age: number // 私有属性定义一遍使用 _ 下划线
// 手写get和set方法
setAge(age: number) {
this._age = age
}
getAge() {
return this._age
}
// 手写存取器
set age(age: number) {
this._age = age
}
get age() {
return this._age
}
// 构造方法
constructor(name: string, age: number) {
this.name = name
this._age = age
}
}
const p = new Person('张三', 18)
p.setAge(28) // 调用setAge方法赋值属性
console.log(p.getAge()) // 28;
p.age = 38 // 使用存取器
console.log(p.age) // 38
静态 static
class Student {
static school: string = '家里蹲' // 静态属性
name: string
constructor(name: string) {
this.name = name
}
static study() {
// 静态方法
console.log('学习')
}
say() {
console.log('说话')
}
}
const s = new Student('张三')
const s2 = new Student('李四')
const s3 = new Student('王五')
s.say()
// s.study() // 不可以通过对象调用静态方法
Student.study() // 通过类调用静态方法
console.log(Student.school) // 静态属性
抽象类
// 声明抽象类,要有抽象方法,必须是抽象类
abstract class Person {
name: string
// 构造方法
constructor(name: string) {
this.name = name
}
// 抽象方法
abstract run(): void
}
// 定义子类,如果不重写抽象方法,子类必须也是抽象类
abstract class Demo extends Person {}
// 实现抽象方法,定义普通类
class Demo2 extends Person {
// 重写抽象方法
run(): void {
console.log('run')
}
}
接口
// 声明接口
interface Demo {
hello: string // 接口可以定义属性
// 接口中的抽象方法
fun(): void
}
interface Demo2 {
fun2(): void
}
// 接口可以多继承
interface Demo3 extends Demo, Demo2 {
fun3(): void
}
// 实现接口,必须要实现所有抽象方法和属性
class Demo4 implements Demo3 {
hello: string = 'hello'
fun3(): void {
throw new Error('Method not implemented.')
}
fun(): void {
throw new Error('Method not implemented.')
}
fun2(): void {
throw new Error('Method not implemented.')
}
}
// 使用别名灵活性
type myObj1 = { name: string } | { age: number }
type myObj2 = { name: string } & { age: number }
const obj1: myObj1 = { name: 'Alice' }
const obj2: myObj1 = { age: 30 }
const obj3: myObj2 = { name: 'Alice', age: 30 }
// 使用接口灵活性,同名的可以合并属性
interface A1 {
name: string
}
interface A1 {
age: number
}
const obj4: A1 = {
name: 'Alice',
age: 30
}
多态
interface USB {
start(): void
run(): void
end(): void
}
// 接口可以作为参数传递
function demo(u: USB) {
u.start()
u.run()
u.end()
}
// 定义子类实现USB接口
class Mouse implements USB {
start(): void {
console.log('鼠标开始工作')
}
run(): void {
console.log('鼠标运行')
}
end(): void {
console.log('鼠标停止工作')
}
}
// 定义子类实现USB接口
class Keyboard implements USB {
start(): void {
console.log('键盘开始工作')
}
run(): void {
console.log('键盘运行')
}
end(): void {
console.log('键盘停止工作')
}
}
// 调用demo函数,子类可以作为参数传递
demo(new Mouse())
demo(new Keyboard())
泛型
// 泛型,传入T类型
function fun<T>(arg1: T, arg2: T, arg3: T): T {
return arg1
}
fun<number>(10, 20, 30)
fun<string>('abc', 'def', 'ghi')
fun<Function>( // 泛型是函数类型
new Function(),
() => {}, // 匿名函数
() => {}
)
// fun<number>(10, 20, "abc") // 报错,必须传同一类型
// 可以传入多个泛型,返回一个泛型
function fun2<T, E, W>(arg1: T, arg2: E, arg3: W): T {
return arg1
}
// 使用传入多个泛型,数字、字符、对象 3种类型
fun2<number, string, { name: string }>(10, 'abc', { name: 'abc' })
// 泛型使用
interface IPerson<T1, T2> {
name: T1
age: T2
}
const person: IPerson<string, number> = {
name: '张三',
age: 18
}
interface IPerson2<T1 = string, T2 = number> {
name: T1
age: T2
}
const person2: IPerson2 = {
name: '张三',
age: 18
}
// 对象使用泛型
class Person<T1, T2> {
name: T1
age: T2
sex: T1
constructor(name: T1, age: T2, sex: T1) {
this.name = name
this.age = age
this.sex = sex
}
}
const p = new Person('张三', 18, '男')
const p2 = new Person<string, number>('张三', 18, '男')
const p3: Person<string, number> = new Person('张三', 18, '男')
Webpack打包TS文件
创建test目录来测试一下,vscode打开这个目录
npm init -y
创建package.json文件
安装WebPack:
npm i webpack webpack-cli webpack-dev-server -D
-D
是安装开发环境,只用于开发环境
安装好以后查看package.json
安装好了这3个工具,然后需要创建webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 需要下载安装插件
module.exports = {
entry: './src/index.ts', // 入口文件
output: { // 输出到哪
path: path.resolve(__dirname, "build"), // 输出目录
filename: 'bundle.js', // 输出文件名
},
module: {
rules: [
{
test: /\.ts$/, // 正则匹配要转换的文件,ts结尾文件
exclude: /node_modules/, // 不转换的目录
use: 'ts-loader' // 使用ts-loader转换
}
]
},
resolve: {
extensions: ['.ts', '.js'] // 解析扩展名,哪种文件可以打包
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html' // 模板文件
})
],
mode: 'development', // 开发模式
}
通过配置文件中的入口文件可以看到入口文件的位置
所以创建src目录,下面创建index.ts文件
function sum(a: number, b: number): number {
return a + b;
}
console.log(sum(1, 2)); // 输出 3
安装html开发插件npm i html-webpack-plugin -D
可以开发中在浏览器中测试
创建一个对应的html文件
执行tsc --init
生成ts的配置tsconfig.json
文件,可以先不修改,就默认的就行
安装tsc和ts-loader
npm i typescript ts-loader -D
-D
是安装开发环境,只用于开发环境,上线时候不用这个插件
执行打包命令:webpack
测试编译好的js文件
如果在浏览器中测试查看,终端窗口使用命令webpack serve
这是开发环境的命令,热加载的效果,会及时生效
执行后浏览器打开http://localhost:8080/
然后查看浏览器的控制台查看效果
这个命令只在开发时使用,上线还是webpack
命名空间
编写代码时候经常会有同名的函数,可以通过命名空间区别开
// 导入其他ts或js文件,Three.ts文件在test目录下
import { Three } from './test'
namespace One {
// 命名空间外部要使用的,必须导出,使用export
export function add(a: number, b: number): number {
return a + b
}
export const hello: string = 'hello'
// 命名空间内部使用自己的函数,导出和不导出都可以使用
console.log(add(1, 2))
}
namespace Two {
export function add(a: string, b: string): string {
return a + b
}
}
// 测试当前文件命名空间
console.log(One.hello)
console.log(One.add(5, 5)) // 命名空间外部要使用,必须导出
console.log(Two.add('hello', ' world'))
// 测试其他文件命名空间
console.log(Three.sub(10, 5)) // 5
// 要想其他文件中使用,要声明导出
export namespace Three {
export function sub(a: number, b: number): number {
return a - b
}
}
描述文件
开发中经常引入第三方组件,有ts和js文件,调用api时候会有问题和冲突,可以通过.d.ts
后缀的文件区分处理
项目中不管在哪个位置写的以.d.ts
结尾的文件,都是描述文件,作用就是开发过程中保证编译不出错
实例:
随便创建一个js文件,模拟当成第三方包
let ew_host = 'http://localhost:3000';
const ew_hello = 'hello world';
function shop(a, b) {
return a + b;
}
let Person = {
this.name = name;
this.age = age;
}
比如使用的时候:报编译错误
console.log(ew_host)
console.log(ew_hello)
console.log(shop(1, 2))
const p = new Person('张三', 20)
console.log(p.name)
创建一个.d.ts
文件
declare let ew_host: string
declare const ew_hello: string
declare function shop(n: number, m: number): number
declare class Person {
name: string
age: number
constructor(name: string, age: number): void
}
在ts
文件中使用,就不会报编译错误了
更多其他的描述可以参考一下:
Vue3和TS结合
先随便创建一个目录,比如test目录,在这个目录的命令行窗口执行npm i -g @vue/cli
安装vue3
如果之前已经安装了vue3版本,可以执行命令vue -V
查看版本
确认安装了vue客户端,就可以创建vue3项目:vue create vue_ts
然后使用上下键选择:手动选择组件
然后可以进行选择可选的组件
下面的操作就可以根据提示慢慢来了
知道操作结束,自动会生成一个vue3的包含ts的项目
进入到项目的目录下,参考package.json
的配置文件启动项目测试
vue3支持ts的组件
参考这个HomeView.vue
的路由组件,以这个为父组件,
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<!-- 给子组件传入属性 字符类型和对象类型-->
<MyComponent hello="hello MyComponent" :title="{ value: 'abc', color: 'red' }"></MyComponent>
<MyComponent2 hello="hello MyComponent" :title="{ value: 'abc', color: 'green' }"></MyComponent2>
</div>
</template>
// 设置成ts
<script lang="ts">
import MyComponent from '@/components/MyComponent.vue';
import MyComponent2 from '@/components/MyComponent2.vue';
import { defineComponent } from 'vue'; // 导入vue的ts组件
export default defineComponent({
name: 'HomeView',
components: {
MyComponent,
MyComponent2
},
});
</script>
然后编写子组件
<template>
<div>
<h1>MyComponent</h1>
num = {{ num }}
<br>
<div v-for="item in goods" :key="item.id">
{{ item.id }}. {{ item.name }} - {{ item.price }}
</div>
<!-- 使用父组件传入进来的值,也就是下面props里面接收的 -->
<h1 :style="{ color: title.color }">{{ hello }} ==> {{ title.value }}</h1>
</div>
</template>
<script lang="ts">
// 导入vue的ts组件
import { PropType, defineComponent, ref } from 'vue';
// 导入定义好的模块
import { IProduct, titleInfo } from '../types';
export default defineComponent({
name: 'MyComponent',
// 接收父组件传来的值
props: {
hello: {
type: String, // 父组件传来的类型必须是字符串
required: true // 必传
},
title: {
type: Object as PropType<titleInfo>, // 父组件传来的类型必须是对象,使用PropType是为了确保类型安全,泛型的效果,然后断言
default() {
return {
value: '默认值',
color: 'red'
}
}
}
},
setup() {
let num = ref(10); // 引入的值 10
let goods = ref([] as IProduct[]); // 断言引入的类型
// 模拟数据
goods.value = [
{ id: 1, name: '商品1', price: 100 },
{ id: 2, name: '商品2', price: 200 },
{ id: 3, name: '商品3', price: 300 }
];
return {
num,
goods,
}
},
})
</script>
<template>
<div>
<h1>MyComponent2</h1>
num = {{ num }}
<br>
<div v-for="item in goods" :key="item.id">
{{ item.id }}. {{ item.name }} - {{ item.price }}
</div>
<h1 :style="{ color: title.color }">{{ hello }} ==> {{ title.value }}</h1>
</div>
</template>
<!-- 指定域为setup,就可以不用写 setup() 的配置了 -->
<script lang="ts" setup>
import { PropType, defineProps, ref } from 'vue';
import { IProduct, titleInfo } from '../types';
// defineProps 和 props 配置一个意思,是接收父组件传来的值
defineProps({
hello: {
type: String, // 父组件传来的类型必须是字符串
required: true // 必传
},
title: {
type: Object as PropType<titleInfo>, // 父组件传来的类型必须是对象
default() {
return {
value: '默认值',
color: '#000'
}
}
}
});
let num = ref(10);
let goods = ref([] as IProduct[]);
goods.value = [
{ id: 1, name: '商品1', price: 100 },
{ id: 2, name: '商品2', price: 200 },
{ id: 3, name: '商品3', price: 300 }
];
// 因为不是写在配置的 setup() 里,所以也不用写return返回了
</script>
要使用的对象定义成了两个模块
最终效果
Vue3 + TS + Element-Plus
Element-Plus官网地址:https://element-plus.gitee.io/zh-CN/
参考官网文档,先安装element-plus组件:npm install element-plus --save
然后使用,在main.ts
引入使用组件element-plus和样式
根据官网推荐的按需引入的自动导入
安装组件:npm install -D unplugin-vue-components unplugin-auto-import
我的项目上webpack打包的,就用webpack打包方式
编写webpack.config.js
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
module.exports = {
// ...
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
}
然后启动项目npm run serve
使用element-plus组件
修改这个组件内容,查看效果
比如把按钮组件的代码复制过来
覆盖在AboutView.vue文件中
然后去页面看效果
点到对应的页面路由
效果出来