ts简单介绍使用
- 安装ts
npm install typescript -g - 查看版本号
tsc -v tsc -w将ts文件编译成js文件- 然后使用
node xx.js执行 - 执行
tsc --init会生成tsconfig.json配置文件 - 在配置文件中
strict: 'false'关闭严格模式
let v1: void = null //严格模式下回报错
let v2: void = undefined
let n: null = null // 严格模式下null 和undefined不能穿插赋值
let u: undefined = undefined // 严格模式下null 和undefined不能穿插赋值
调试ts
- 安装一些库用来调试typescript比较方便
- 如
npm i ts-node -g
- 先安装
npm i xmzs -g用来切换npm源,
- 安装了xmzs之后可以利用
mmp ls查看npm所有的源,
mmp use切换源,
npm i ts-node -g装ts-node就相当快了,
- 然后通过
npm init -y生成一个package.json文件 - 然后装一个声明文件
npm i @types/node -D - 然后
ts-node xxx.ts就可以运行ts文件了
介绍ts
- any任意类型, unknown不知道的类型
- Object
- Number String Boolean
- number string boolean
- 1 false
- never
- 注意点
let a:unknown = {name: 'aaa',func: () => {}}
a.func// 报错
a.name// 报错
let b: any = {name: 'aaa',func: () => {}}
b.func// 可以调,但是没有提示
b.name// 可以调,但是没有提示
- // unknown 类型只能赋值给any和unknown,不能赋值给别的类型
- // unknown 类型没有办法读任何属性,方法也不可以调用
- // any 可以调属性和方法,但是没有提示
- // unknown 比any更加安全
小知识
- Object (大写O)
Object是原型链的顶端,所以可以等于所有类型
- object (小写o)
一般用于泛型约束
let a:object = '123' // 错误
let a:object = 123 // 错误
let a:object = false // 错误
let a:object = [] // 正确
let a:object = {} // 正确
let a:object = () => {} // 正确
// {} 与 Object一样,也是只所有的类型,所以下面都正确
let c: {} = '123'
let c: {} = 123
let c: {} = false
let c: {} = []
let c: {} = {}
let c: {} = () => {}
// 字面量模式虽然可以赋值任意类型,但是赋值之后是不能添加的
let d: {} = {name: 1}
d.age = 13 // 报错
接口和对象类型
// interface 要长得一模一样,不能多属性也不能少属性
interface Axxsxx {
name: string,
age: number,// 可写可不写
readonly cb: () => boolean,
[propName:string]:any //这样下面就可以随便定义了,但是必须要有name
}
// 接口继承extends 后面也可以继承多个
interface Bxx extends Axxsxx {
gender: string
}
let a: Axxsxx = {
name: 'Alice',
cb: () => {
return false
}
}
interface Fn {
(name:string): number[] // 参数 : 返回值
}
const fn:Fn = function (name: string) {
return [1]
}
- interface遇到重名的话会自动合并到一块,并不会报错
- interface继承,后面可以继承多个
数组类型
- 定义数组
let arr: number[] = [1,2,3]
let abb: boolean[] = [false,true]
let ayy: Array<boolean> = [true, false]
- 接口在数组中的使用
interface X = {
name: string,
age?: number
}
let arr:X[] = [{name: 'Tom'}, {name: 'Alice'}]
- 二维数组和大杂烩数组
// 二维数组
let arr: number[][] = [[1],[2]]
let abb: Array<Array<number>> = [[1],[2]]
// 大杂烩数组
let all: any[] = [1,false, 'hhh']
// 也可以元组,一个一个去对应,但是比较烦人啰嗦
let bll: [number, boolean, string] = [1,false, 'hhh']
- 数组在函数参数中
function f(...args: any[]) {
}
function f(...args: number[]) {
}
function f(...args: string[]) {
}
f([1,false,'hhh'])
f([1,2,3])
f('e','r')
- 数组中的arguments,有一个内置的类型定义, 可以直接使用
function f() {
let a:IArguments = arguments
}
// IArguments原理就是一个接口,类似下面的
interface A {
calle: Function
length: number
[index:number]: any
}
函数扩展
- 定义函数的参数和返回值
function add(a: number, b: number): number {
return a + b
}
const add1 = (a:number,b:number):number => a + b
- 函数的默认值和可选参数
function add(a: number=10, b: number=20): number {
return a + b
}
// 问号表示可选参数,但是可选参数和默认值不能同时使用
function add(a: number=10, b?: number): number {
return a + b
}
interface User {
name: string
age: number
}
function add(user: User):User {
return user
}
- ts增强,js中无法使用的
interface Obj {
user: number[]
add: (this.Obj,num: number) => void
}
let obj: Obje = {
user: [1,2,3],
add(this:Obj, num: number) { // 传参数的时候忽略掉this就可以了,参数从第二个开始
this.user.push(num)
}
}
obj.add(55)
- 函数重载
let user: number[] = [1,2,3]
function findNum():number[] // 没有传入参数,查全部
function findNum(id: number):number[] // 传入了id,就是查询单个
function findNum():number[] {}
function findNum(id?: number[] | number) {
if(typeof id == 'number) {
}else if(Array.isArray(id)) {
}else {
}
} // 传入数组添加
- 联合类型
let a: number | string = 123321
let fn = function(type: number | boolean) : boolean{
return !!type
}
- 交叉类型
interface Pople {
name: string
age: number
}
interface Man {
gender: number
}
// 类似extens 会合起来name,age,gender
const xiaoman = (man:Pople & Man):void => {
return man
}
- 类型断言(两种写法)
// 1
let fn = function(num: number | string):void {
console.log((num as string).length)
}
fn('123')
// 2
interface A {
run: string
}
interface B {
build: string
}
let fn = (type: A | B) => {
console.log((<A>type).run)
}
const fn = (type: any):boolean => {
return type as boolean
}
内置对象
let num:Number = new Number(1)
let date: Date = new Date()
let reg: RegExp = new RegExp(/\w/)
let err: Error = new Error('错了')
let xhr: XMLHttpRequest = new XMLHttpRequest()
//HTML(元素)Element 或者 HTMLElement 或者 as Element
let div:HTMLDivElement = document.querySelector('div')
let div:NodeList = document.querySelector('div') // 循环
let div:NodeListOf<HTMLDivElement | HTMLElement> = document.querySelector('div')
- 浏览器 window相关的类型
let local:Storage = localStorage
let lo: Location = location
let promise: Promise<number> = new Promise((r)=> r(1))
let cookie:string = document.cookie
Class类
interface Options {
el: string | HTMLElement
}
interface VueCls {
options: Options
init(): void
}
interface Vnode {
tag: string
text?: string
children?: Vnode[]
}
class Dom {
createElement(el: string) {
return document.createElement(el)
}
setText(el: HTMLElement,text: string | null) {
el.textContent = text
}
render(data: Vnode) {
let root = this.createElement(data.tag)
if(data.children && Array.isArray(data.children)) {
data.children.forEach(item => {
let child = this.render(item)
root.appenChild(child)
}
}else {
this.setText(root, data.text)
}
}
}
class Vue extends Dom implements VueCls {
options: Options
constructor (options: Options) {
super()
// super.render 找到父类的方法,super可以给父类的constructor进行传参,原理:prototype.constructor.call
// static 静态方法通过类直接调用
this.options = options
this.init()
}
init(): void {
let data: Vnode = {
tag: 'div',
children: [{
tag: 'section',
text: '这里是子节点1'
}]
}
let app = typeof this.options.el == 'string' ? document.querySelector(this.options.el) : this.options.el
app.appendChild(this.render(data))
}
}
new Vue({
el: '#app'
})
- class修饰符
- readonly 只读
- private 只能在自己内部使用
- protected 只能自身合子类使用
- public 默认的,所有的方法,大家都可以用
- get set
class Ref {
_value: any
constructor(value: any){
this._value = value
}
get value() {
return this._value + 'vjsl'
}
set value(newVal) {
this._value = new Val + 'hhhhh'
}
}
const ref = new Ref('9skjf')
- 抽象类
abstract class Vue {
name: string
constructor (name?:string) {
this.name = name
}
getName(){
return this.name
}
abstract init(name: string) : void
}
class React extends Vue {
constructor(){
super()
}
init(name: string) {
}
setName(name: string) {
this.name = name
}
}
const react = new React()
react.setName('hhh王小溜溜')
- 元组类型
let arr: readonly [number, boolean] = [1, false ]
arr[0] = 999 // 有readonly的不能修改,去掉readonly就可以修改了
- 枚举类型
enum Color {
red,
green,
blue
}
console.log(Color.red)//0
console.log(Color.green)//1
console.log(Color.blue)//2
// 如果赋值了,就全部赋值,不然无法递增
enum Color1 {
red='red',
green='green',
blue='blue'
}
- 接口枚举
//const enum Color { // 用了const就会编译成常量
enum Color {
no = 'no'
yes = 1 // 数字支持反向映射
}
interface A {
red: Color.yes
}
let obj: A = {
//red: 1
red: Color.yes
}
- 类型推论和类型别名
type s = string | number
let a: s = '234'
- type 和 interface的区别
type s = number[] & B
interface A extends B {}
interface B {}
// type 不能使用extends关键字继承智能使用 &合在一起 |联合
// interface遇到重名会合并
- type的高级用法
type num = 1 extends number ? 1 : 0 // 1
// extends 在type中是包含的意思,左边的值是右边的子集
type num = 1 extends any ? 1 : 0 // 1 //1
type num = 1 extends unknow ? 1 : 0 // 1
type num = 1 extends Number ? 1 : 0 // 1
type num = 1 extends Object ? 1 : 0 // 1
type num = 1 extends never ? 1 : 0 // 0
- 不存在的类型
// 1
type A = string & number // never
// 2
function mm():never {
throw new Error('这个函数一执行就会报错,使用never类型更好')
}
// 3
function sixunhuan():never {
while(true) {
}
}
// 联合类型中的never会被直接忽略
type B = void | number | never // 这里的never会被直接忽略
- 用never进行兜底逻辑的校验
type A = '跳舞' | '唱歌' | '名族舞'
function skill(value: A) {
switch (value) {
case '跳舞':
break
case '唱歌':
break
case '名族舞':
break
default:
const error: never = value
break
}
}
symbol类型
let a1:symbol = Symbol(1)
let a2:symbol = Symbol(2)
Symbol.for('lllxxxmmm') === Symbol.for('lllxxxmmm') // true
// for获取全局找有没有注册过这个key,如果有直接拿来用,不会创建新的,如果没有就去创建
// symbol可以解决属性重名问题
let obj = {
name: 1,
[a1]: 111,
[a2]: 222
}
// for in 不能督导symbol
// Object.keys(obj) 也读不到symbol
// Object.getOwnPropertyNames(obj) 也读不到symbol
// Object.getOwnPropertySymbols(obj) 只能读到symbol
// Reflect.ownKeys(obj) 即能读到普通属性也能读到symbol
- 生成器
function* gen() {
yield 'xxxx'
yield Promise.resolve('夏天快过去了')
}
const m = gen()
m.next()
m.next()
- 迭代器
let set: Set<number> = new Set([1,2,3,4,5]) // 会天然的去重
let map: Map<any, any> = new Map()
let arr = [2,2,2,4]
map.set(Arr,'数字')
console.log(map.get(Arr)
// 手写迭代器
const each = (value: any) => {
let It: any = value[Symbol.iterator]()
let next: any = {done: false}
while(!next.done) {
next = It.next()
if(!next.done) {
console.log(next.value)
}
}
}
each(set)
each(map)
- 迭代器的语法糖 for ... of ...
// for of 的底层就是迭代器
// for of 不能用在对象上面,因为对象没有迭代器
// 数组解构底层原理也是去调用迭代器
- 手写对象支持迭代器
let obj = {
max: 100,
name: '夏天',
current: 0
[Symbol.iterator]() {
return {
max: this.max,
current: this.current,
next() {
if(this.current == this.max) {
return {
value: undefined,
done: true
}
}else {
return {
value: this.current++,
done: false
}
}
}
}
}
}
for(let i of obj){
console.log(i)
}
泛型
- 泛型也可以称之为动态类型
function myname<T>(a:T,b:T):Array<T> {
return [a,b]
}
type A<T> = string | number | T
let a: A<undefined> = undefined
let b: A<string> = 'good'
interface Data<T> {
msg: T
}
let data:Data<number> = {
msg: 100
}
let data2:Data<string> = {
msg: '夏天好热,但是夏天真的很好玩'
}
function add<T = number,K = string>(a:T,b:K):Array<T|K> {
return [a,b]
}
- 泛型的使用
const axios = {
get<T>(url: string):Promise<T> {
return new Promise((resolve,reject) => {
let xhr: XMLHttpRequest = new XMLHttpRequest()
xhr.open('GET',url)
xhr.onreadstatechange = () => {
if(xhr.readyState == 4 && xhr.status == 200 ){
resolve(JSON.parse(xhr.responseText))
}
}
xhr.send(null)
})
}
}
interface Data {
message: string
code: number
}
axios.get<Data>('./data.json').then(res => {
console.log(res)
})
- 泛型约束
function add<T extends number>(a,b) {
return a + b
}
interface Len {
length: number
}
function fn<T extends Len>(a: T) {
console.log(a.length)
}
let obj = {
name: '阿狸',
sex: '女'
}
type Key = keyof typeof obj
function ob<T extends object, K extends keyof T> (obj: T, key: K) {
return obj[key]
}
// keyof的高级用法
interface Data {
name: string
age:number
gender: string
}
type Options<T extends object> = {
[Key in keyof T]?: T[Key] // 循环加了? 或者加readonly
}
-
ts的配置文件
-
- 通过
tsc --init就可以生成一个配置文件
- 通过
-
namespace命名空间 // 编译成js 用node运行
// 在文件中用export导出,说明这是一个模块,或者加namespace,说明是一个模块,如果这两种方法都不采用,那么会被认为是全局的
// namespace是可以嵌套的
// 重名会被合并
namespace A {
export const a = 2
expoet namespace C = {
export const D = 8
}
}
// 起别名
import aa = A.C.D
console.log(A.a)
console.log(aa) // 8
- 三斜线指令
///<reference path="index2.ts"/>
namesapce A {
}
- 第三方文件声明文件的引入
-
- 如果你安装的库有自带了声明文件是不需要额外安装的,但是如果没有那么可以去社区找一找,用
npm i --save-dev @types/express,如果存在的话这个命令是可以行得通的,否则,反之。
- 如果你安装的库有自带了声明文件是不需要额外安装的,但是如果没有那么可以去社区找一找,用
-
- 但是有些冷门的库社区里面也没有声明文件,那么只能手写
-
-
首先新建文件
xxx.d.ts
-
declare module 'express' {
interface Router {
}
interface App {
}
interface Express {
}
}
// 也可以扩展全局变量
declare let a: number
- Mixins 混入(持续更新中。。。。。。)
const a: string = 'hhhhh'
const num: number = 999 // NaN Infinity
const d: string = null
const e: boolean = null // 严格模式下不能为空
const g: void = null //非严格模式可以是undefined/null 严格模式是null
const f: null = null
const h: undefined = undefined
const i: symbol = Symbol('jj')
const error: string = '111'
const j: object = [] // [] {} function 这里的object可以是这些类型
const l: {} = {foo: 'foo'} // 这里就只能是字面量类型
const m: { foo: number, bar: string} = {foo: 100, bar: 'beautiful'}// 一一对应
// 数组
const arr1: Array<number> = [1,2,3,4] //纯数字组成的数组
const arr2: number[] = [1,2,3,4]
function sum(...args:number[]) {
return args.reduce((prev,current) => prev + current,0)
}
// sum(1,'foo')// 报错,
// 元组类型
const tuple: [number, string] = [18, 'aaa']
const age1 = tuple[0]
const [age, name1_3334] = tuple
enum PostStatus {
draft= 0,
unpublish=1,
publish=1
}
const post = {
title: '是标题',
content: 'typescript study',
status: PostStatus.draft
}
function func1(a: number,b:number, ...rest: number[]): string {
return 'string'
}
// func1('3', '33')
const func2 = function(a: number, b: number) : string{
return 'string'
}
let fc: any = 'kkkk' // 不会有任何类型检查,不要轻易使用
// 断言
const num1 = 199
let num2 = num1 as number
let num3 = <number>num1 // 这种方式可能于jsx的语法有冲突 不推荐
// 接口 Interface
interface Post{
title: string,
klass: number,
subtitle?: string // 可有可无
readonly summary: string // 只读属性
}
function printPost(post: Post) {
console.log(post.title)
}
printPost({title: 'xxx',klass: 123,summary: 'jjjjjj'})
interface Cache {
[prop: string]: string
}
// let a: Cache = {a: 'aa'}
class Person {
public name: string // 默认就是public
private age: number // 私有
protected readonly gender: boolean // readonly放在访问修饰符的后面
constructor(name: string, age: number) {
this.name = name
}
}
// private protected 外部无法访问,子类可以放问protected
class Student extends Person {
private constructor(name: string, age: number) {
super(name, age)
}
static create(name: string, age: number) {
return new Student(name, age)
}
}
const tom = Student.create('jack', 18)
// 接口
interface Eat {
eat(food: string): void
}
interface Run {
run(distance: number): void
}
class Person implements Eat,Run {
eat(food: string): void {
}
run(distance: number): void {
}
}
class Animal implements Eat, Run {
eat(food: string): void {
}
run(distance: number): void {
}
}
// 泛型
function createNumber(length: number, value: number): number {
const arr = Array<number>(length).fill(value)
return arr.length
}
function createString(length: number, value: string): number {
const arr = Array<string>(length).fill(value)
return arr.length
}
function create<T>(length: number, value: T): T[] {
const arr = Array<T>(length).fill(value)
return arr
}
// 把不明确的定义成一个参数
const res = createNumber(3, 100)
// 兼容一些第三方模块
// import { camelCase } from 'lodash'
// declare function camelClass(input:string): string