typescript

348 阅读14分钟

typescript文档

先装上typescript包,新增脚本

npm i typescript 装ts包
tsc --init 生成tsconfig.js配置文件
@types开头的包 都是ts声明文件,可以进入node_modules/@types/xx/index.d.ts进行查看
ts-loader 可以让wwebpack使用ts的标准配置文件tsconfig.json编译ts代码
"scripts": {  "dev":"tsc index.ts --watch"  }

npm run dev 执行命令,将index.ts文件   翻译为index.js文件

ts声明方式

布尔值

let flag:boolean = true;

数字

let count:number = 1;
let hexLiteral: number = 0xf00d;//十六进制数字
let binaryLiteral: number = 0b1010; //二进制数字
let octalLiteral: number = 0o744;//八进制数字

字符串

let name:string = 'zanlan';

数组 方式一

//直接使用[ ] 前面加上内部的类型标识
let list: number[] = [1, 2, 3];

数组 方式二

// 数组泛型  Array<元素类型>
let list: Array<number> = [1, 2, 3];

元组 Tuple

//元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
let x: [string, number] = ['hello', 10];

**类型一 | 类型二 | 类型三 .... **

//多种类型并集  只需要满足一种就行
let age:number | undefined | null = 10

枚举

//默认序号 从0开始
enum Types { type1, type2, type3 }
let g1:Types = Types.type1    // => 0
let g2:Types = Types.type2    // => 1
let g3:Types = Types.type3    // => 2
console.log(Types)
{
    '0': 'type1',    '1': 'type2',    '2': 'type3',
    type1: 0,    type2: 1,    type3: 2
}
//自定义序号 从1开始 后面序号自动递增
enum Types { type1 = 1, type2, type3 }  
//完全自定义序号
enum Types { type1 = 1, type2 = 3, type3 = 5 }

any

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
let list: any[] = [1, true, "free"];
list[1] = 100;

void

//标识函数没有返回值 及时有也只能是返回null或者undefined
function greeting(name: string):void {}
//声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null:
let unusable: void = undefined;

Null 和 Undefined

//设置的值时固定的 所以没有太大用处
let u: undefined = undefined;
let n: null = null;
//默认情况下null和undefined是所有类型的子类型。 可以把 null和undefined赋值给number类型的变量。
let num1:number = undefined;
let num2:number = null;
// 指定strictNullChecks为true时,null和undefined只能赋值给void类型变量
//这种严格情况下,如果确实想给某个变量允许给其设置null和undefined,可以这样,如下
let num3:string | null | undefined = undefined;

never

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
    return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
    while (true) {
    }
}

类型断言

let znames: number | string | null ;
function ww(){
    console.log((znames as string).length)
}
znames = "13123"
ww()
let someValue: any = "this is a string";
let strLength1: number = (<string>someValue).length;//和下面的效果一样
let strLength2: number = (someValue as string).length;

不可以这样写

let a = 1
let b = 'b'
let c: a | b = 1

可以这样写

let c: 1 | 'b' = 1

可以这样写

type cType = 1 | 'b'
let c:cType = 1

函数类型声明

type GetFunction = (x:string,y:string)=>string;

let getUsername:GetFunction = function(f:string,l:string):string{
    return f+l
}

函数形参声明

function print(name:string,age?:number):void{} // age参数可以不传
function print(name:string,age:number=24):void{} // age默认值为24

函数形参聚合为数组

function sum(...number:Array<number>){
    console.log(number)
}

sum(1,2,3,4)

函数一个形参多种类型

function attr(val:string|number){
    if(typeof val === 'string'){
        console.log(val,'string')
    }else{
        console.log(val,'number')
    }
}

函数一个形参多种类型

function attr(val:string):void;
function attr(val:number):void;
function attr(val:any){
    if(typeof val === 'string'){
        console.log(val,'string')
    }else{
        console.log(val,'any')
    }
}
attr('asdfad')
attr(123)

声明实例属性myname

class Person {
    myname:string;     // strictPropertyInitialization 设置为false,否则报错
}

声明实例属性myname

class Person {
    myname:string = 'zanlan';  不会报错
}

声明实例属性myname

class Person {
    myname:string';     //不会报错
    constructor(x:string){
        this.myname = x
    }
}

访问控制修饰符 public protected private

  • public 都可以访问到,没有限制
class Animal{
    public readonly name:string = 'x'
    constructor(name:string){
        this.name = name
    }
}
class Dog extends Animal{
    getName(){
        console.log(this.name)//能访问
    }
}
let a = new Animal('zanlan')
console.log(a.name)//能访问
  • protected 只能是在当前的class内部使用,和他的子类的内部使用
class Animal{
    protected readonly name:string = 'x'
    constructor(name:string){
        this.name = name
    }
    getName(){
        console.log(this.name)//能访问 输出 "zanlan"
    }
}
class Dog extends Animal{
    getName(){
        console.log(this.name)//能访问 输出 "zanlan"
    }
}
new Animal('zanlan').getName()//能访问
new Dog('zanlan').getName()//能访问

new Animal('zanlan').name // 报错 
new Dog('zanlan').name // 报错

//报错信息为
//Property 'name' is protected and only accessible within class 'Animal' and its subclasses.
  • private 只能是在当前的class内部使用,和他的子类的内部使用
class Animal{
    private readonly name:string = 'x'
    constructor(name:string){
        this.name = name
    }
    getName(){
        console.log(this.name)//能访问 输出 "zanlan"
    }
}
class Dog extends Animal{
    getName(){
        console.log(this.name)// 报错
    }
}
new Animal('zanlan').getName()//能访问
new Animal('zanlan').name // 报错 
/* 报错信息 为 */
Property 'name' is private and only accessible within class 'Animal'.

重写(override)--子类重写继承自父类中的方法

class Animal {
    speak(word:string):string{
        return '叫' + word
    }
}
class Cat {
    speak(word:string):string{
        return '猫叫' + word
    }
}
let cat = new Cat()
console.log(cat.speak('喵喵喵'))

重载(overload)-- 同一个函数提供多个类型定义

function double(val:number):number
function double(val:string):string
function double(val:any):any{
    if(typeof val === 'number'){
        return val*2
    }
    return val+val
}

接口interface

只要满足接口的必要条件就可以 但是需要用变量赋值 否则必须是充要条件

interface LabelledValue{
	label:string
}
function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);// 不报错

//对象字面量会被特殊对待而且会经过 额外属性检查,当将它们赋值给变量或作为参数传递的时候。 
//如果一个对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误。
printLabel({size: 10, label: "Size 10 Object"});//报错

? 代表是 可以不传的意思

interface Speakable{
  name?:string;
}
let speakman:Speakable = {}

可索引的类型 表示数组

interface UserInterface {
    [index: number]: string
}
let arr: UserInterface = ['zf', 'jg']
console.log(arr) // ['zf', 'jg']

可索引的类型 表示对象

interface UserInterface2 {
	name:string;
    [index: string]: string;
}
let obj2: UserInterface2 = { name: 'zhufeng' }
console.log(obj2)// { name: 'zhufeng' }

接口相互继承用extends class类实现接口特征

interface AnimalLike{
    eat():void;
}

interface PersonLike extends AnimalLike{
    speak():void;
}

// 如果继承多个接口 则用逗号隔开连续写 implements Flying,Eating
class Girl implements PersonLike{ //implements 工具,实现
    eat(){}
    speak(){}
}

class类作为参数传递 接口可以规定class类应该有哪些属性

interface WithNameClass{
    new(name:string):Animal // 在new Animal时,构造函数要传个字符串
    age:number // Animal类的静态属性age 为number  也可以是 Animal.prototype上上的属性
    getAge():void// Animal类的静态属性getAge为number 也可以是 Animal.prototype上上的属性
}
class Animal {
   constructor(public name:string){}
   static age:number = 1; // 声明Animal.age
   getAge(){ // 声明Animal.prototype.getAge
       console.log(Animal.age)
   }
}

function creaetClass(clazz:WithNameClass,name:string){  
    //书写clazz会报错 解决办法是 "noImplicitAny": false
    return new clazz(name)
}
console.log(creaetClass(Animal,'123'))//Animal { name: 'zanlan' }

使用implements,接口只能修饰class类的静态属性 以及类的prototype属性,不可以修饰实例属性

interface ClockConstructor {
  new (hour: number, minute: number);
}
class Clock implements ClockConstructor {//无法修饰constructor内数属性,所以报错
  constructor(h: number, m: number) { }
}

//下面是解决办法  只有当被new时候,才会修饰到constructor函数
interface ClockConstructor {
  new (hour: number, minute: number):Clock;
}
class Clock{//无法修饰constructor内数属性,所以报错
  constructor(h: number, m: number) { }
}

对多个class类的constructor 和属性做统一修饰

interface A {// A为构造函数所用
  new(a: string, b: number): B;
}
interface B {//B为实例方法所用。
  tick();
}
class F1 implements B {
  constructor(a: string, b: number) { }
  tick() { }
}
class F2 implements B {
  constructor(a: string, b: number) { }
  tick() { }
}

function createF(F: A, a: string, b: number):B {
  return new F(a, b)
}

createF(F1, '1', 1)
createF(F2, '2', 2)

接口继承类

类定义会创建两个东西:类的实例类型和一个构造函数。 因为类可以创建出类型,所以你能够在允许使用接口的地方使用类
class Point {
    x: number;
    y: number;
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};
//接口继承了一个类的类型时,它会继承类的成员  但不包括其实现
class A {
  private state: any;
}
interface F extends A { // F接口
  select(): void;
}
class C implements F {
  state = 1 // 报错 “state”在类型“F”中是私有属性
  select() { }
}

只读属性 readonly ReadonlyArray

interface Point {
    readonly x: number;
    readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!
let ro: ReadonlyArray<any> = ['1', true, 1];
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!

接口修饰 普通函数 规定传参 以及返回值

interface SearchFunc {
  (source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function(source: string, subString: string) {  
  return true;
}

修饰符

public 在TypeScript里,成员都默认为 public。 private 被修饰属性,只能在当前类里面使用,其他地方都不可以用,包括实例 protected 被修饰属性,只能在当前类,以及子类里面使用,其他地方都不可以用,包括实例 protected constructor

class F {
  protected constructor() {  }
}
new F(); // 错误: 'F' 的构造函数是被保护的,只能通过继承才能访问F的构造函数

两个不相干的类 内部有private或者protected属性,则它们类的实例不可以相互做赋值操作

class A {
  private name: string;
  constructor(theName: string) { this.name = theName; }
}
class B extends A {
  constructor() { super("B"); }
}
class C {
  private name: string;
  constructor(theName: string) { this.name = theName; }
}
let a = new A("aaaaa");
let b = new B();
let c = new C("ccccc");
a = b;  
a = c; // 错误: a 与 c 不兼容.

readonly 只读属性必须在声明时或构造函数里被初始化。

class Octopus {
  readonly name: string;
  readonly age: number = 8;
  constructor (v: string) {
      this.name = v;
  }
}

存取器

TypeScript支持通过getters/setters来截取对对象成员的访问 只带有 get不带有 set的存取器自动被推断为 readonly

class Person {
    myname:string = 'zanlan';  //声明this.myname = "zanlan"
    get name() {               //声明 Person.prototype.name 
        return this.myname.toUpperCase()
    }
    set name(newName: string) {
        this.myname = newName
    }
}

一般情况下回报错

 Accessors are only available when  targeting ECMAScript 5 and higher

解决办法是 指定编译版本为es5的

tsc index.ts --watch -t es5

抽象类

抽象类只能被继承,且其内部申明的抽象方法,必须在子类中实现

abstract class A {
  f1(): void {}
  abstract f2(): void; // 必须在派生类中实现
}
class B extends A {
  f2(): void {}
  f3(): void {}
}
let a: A; // 允许创建一个对抽象类型的引用
a = new A(); // 错误: 不能创建一个抽象类的实例
a = new B(); // 允许对一个抽象子类进行实例化和赋值
a.f1();
a.f2();
a.f3(); // 错误: 方法在声明的抽象类中不存在

typeof class 取class类的类型

typeof A,意思是取A类的类型,而不是实例的类型。 或者更确切的说,"告诉我 A标识符的类型",也就是构造函数的类型。 这个类型包含了类的所有静态成员和构造函数

class A {}
let B: typeof A = A;

TypeScript 函数传参

不可多传 不可少传,除非是?号参数

function F(a: string, a: string) {
  return a+ " " + b;
}
F("a");                  // error, too few parameters
F("a", "b", "c");  // error, too many parameters
F("a", "b");         // ah, just right

参数合并

function F(a: string, ...arr: string[]) {
    return a + " " + arr.join(" ");
}
F("a", "b", "c", "d");

泛型

T变量表示类型,捕获用户传入的类型 方式一 需要用户手动传类型

function F<T>(length: number, value: T): T[] {}
F<string>(3, 'x')

方式二 系统自动识别类型 这种更加普遍

F(3, 'x')

如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入T的类型,在一些复杂的情况下,这是可能出现的。

泛型 在 接口里应用

interface InterF<T> {
    (arg: T): T;
}
function F<T>(arg: T): T {
    return arg;
}
let myIdentity: InterF<number> = F;

多个泛型

function F<A, B>(arr: [A, B]): [B, A] {
    return [arr[1], arr[0]]
}
F([1, 'a'])  //['a',1]

泛型继承接口 接口描述泛型的属性类型

interface Len{
    length:number
}
function logger<T extends Len >(val:T){
    console.log(val.length)
}
logger('xxx-')

接口使用泛型

interface Cart<T>{
    list:T[]
}
let cart:Cart<number> = {list:[1,2,3]}

泛型别名

type Cart<T> = { list: T[] } | T[]
let c1: Cart<number> = { list: [1, 2, 3] }
let c2: Cart<number> = [1, 2, 3]

类数组

let root = document.getElementById('root')
let children:HTMLCollection = root!.children;
let childNodes:NodeListOf<ChildNode> = root!.childNodes

strictNullChecks为true,则代码中不能调用可能为null的变量的属性了

function getFirstLetter(str:string | null){  
  console.log(typeof str.trim())//str可能为null,所以会报错  
}
getFirstLetter(null)

?. 是否为undefined/null,是则返回undefined

a?.b;
a == null ? undefined : a.b;
let a:A['job'] = { jobname:'程序员' }

type C = A | B;

索引类型查询操作符

interface Person {
    name: string;
    age: number;
    gender: 'male' | 'female'
}
type PersonKey = keyof Person;//索引类型查询
function getValueByKey(p: Person, key: PersonKey) {
    return p[key]
}
let val = getValueByKey({ name: 'zhufeng', age: 10, gender: 'male' }, 'name')
console.log(val)

in 结合keyof,不加问号会报错

类型“{ name: string; }”缺少类型“Part”中的以下属性: age, gender 在这里插入图片描述

in 结合keyof,加了问号后,是允许不传name,age,gender属性

interface Person{
    name:string;
    age:number;
    gender:'male'|'female'
}
type Part<T> = {
    [key in keyof T]?:T[key]
}
let p:Part<Person> = {name:'zanlan'}
console.log(p)// { name: 'zanlan' }

Partial 可以将传入的属性由可选变为可选,具体使用如下

interface A {
    a1: string;
    a2: number;
    a3: boolean;
}
type aPart = Partial<A>;
const a: aPart = {a1:'zanlan'};
console.log(a)//{a1:'zanlan'}

内置的Partial方法实现

type Partial<T> = {
    [p in keyof T]?: T[p];
}

Required可以将传入的属性中的可选项变成必选项,这里用了-?修饰符

interface Person{
    name:string;
    age:number;
    gender?:'male' | 'famale'
}
let p1:Person = {
    name:'zhufeng', //  没有加Required 这里不会报错
    age:10
}
let p2:Required<Person> = {
    name:'zhufeng',
    age:10,
    gender:'male' // 必须要写gender 否则报错
}

内置的Required方法实现

type Required<T> = {
    [p in keyof T]-?: T[p];
}

Readonly通过为传入的属性每一项都加上Readonly修饰符来实现。

在这里插入图片描述 内置的Readonly方法实现

type Readonly<T> = {readonly [p in keyof T]:T[p]}

Pick

interface Animal{
    name:string;
    age:number;
    getName():void;
}
type AnimalSub = Pick<Animal,'name'|'age'>;
let a:AnimalSub = {
    name:'zhufeng',
    age:10
}

实现方法

type Pick<T, K extends keyof T> = {[P in K]: T[P]};

Exclude 除了某某之外

type A = Exclude<string|number|boolean,string>;
let a:A = 10;
let b:A = true;
let c:A = 'xx';//报错
interface Face1{};
interface Face2{};
type C = Exclude<Face1|Face2,Face1>;

Extract 提取出某某有效

type A = Extract<string | number | boolean,string>;
let a:A = 'hello';
let b:A = 1;//报错
let c:A = true;//报错

NonNullable 剔除null 和 undefined类型

type D =NonNullable<string | number | null | undefined>;
let e:D = null;//不能将类型“null”分配给类型“D”
let f:D = undefined;//不能将类型“undefined”分配给类型“D”

ReturnType获取函数类型的返回类型

function getUserInfo(){
    return {name:"zhufeng",age:10}
}
type UserInfo = ReturnType<typeof getUserInfo>;
const userA:UserInfo = {
    name:'zhufeng',
    age:true,//报错
    ww:'fsadf'//报错
}

局部声明全局属性

declare global{ // 代表在局部内 声明全局的属性
    interface String{
        double():string;
    }
    interface Window {
        myname:string;
    }
}

String.prototype.double = function(){
    return this+this
}
console.log('hello'.double())
window.myname = '123'
console.log(window.myname)

export {} // 加了这个 代表是上面的声明是局部的

class和interface区别

class Person {
    name:stirng = 'hello'
}

// Person 既可以作为类使用 也可以做值来使用
let p1 = Person;
let p2 = new Person()

//接口只能作为类使用,不可以作为值赋值给变量
interface Person1 {
    name:string;
}
let p3:Person1;
let p4 = Person1; //报错 “Person1”仅表示类型,但在此处却作为值使用

合并声明

interface Person {//一个文件声明了Person
    name: string;
}
interface Person {//另一个文件也声明了Person  
    age: number;
}
 
let p: Person = { //因为同名  在ts文件使用时候  就会被合并
    name: '123',
    age: 123
}

namespace扩展类,用于表示内部类

class Form {
    username:Form.Item = '';
    password:Form.Item = '';
}
namespace Form{
    export class Item {}
}
let item:Form.Item = new Form.Item()
console.log(item)
enum Color {
    red = 1,
    yellow = 2
}

namespace Color {
    export const green = 4;
    export const purple = 5;
}

console.log(Color.green) // 4

导出接口

注解 Props

export declare type PropType<T> = PropConstructor<T> | PropConstructor<T>[];//vue中导出

export interface ListItem {//自定义的
  avatar: string;
  title: string;
  datetime: string;
  type: string;
  description: string;
  status?: "" | "success" | "warning" | "info" | "danger";
  extra?: string;
}
	
const props = defineProps({
  noticeItem: {
    type: Object as PropType<ListItem>,
    default: () => {title:'123'},//定义default 和 validator 必须用到箭头函数
    validator: (book: Book) => !!book.title
  }
});

{{ props.noticeItem.title }} //定义好props参数 直接使用
{{ props.noticeItem?.extra }} 

定义computed返回类型

computed: {    
    greeting(): string {// 需要注解
      return this.message + '!'
    }
}

泛型表示法

const year = ref<string | number | boolean>("2020");
year.value = true;

父组件 调用子组件方法

import { defineComponent, ref } from 'vue'
const MyModal = defineComponent({
  setup() {
    const isContentShown = ref(false)
    const open = () => (isContentShown.value = true)
    return {
      isContentShown,
      open
    }
  }
})
const app = defineComponent({
  components: {
    MyModal
  },
  template: `
    <button @click="openModal">Open from parent</button>
    <my-modal ref="modal" />
  `,
  setup() {
    const modal = ref()
    const openModal = () => {
      modal.value.open()
    }
    return { modal, openModal }
  }
})

它可以工作,但是没有关于 MyModal 及其可用方法的类型信息。为了解决这个问题,你应该在创建引用时使用 InstanceType:

setup() {
  const modal = ref<InstanceType<typeof MyModal>>()
  const openModal = () => {
    modal.value?.open()
  }
  return { modal, openModal }
}

类型声明 reactive

import { defineComponent, reactive } from 'vue'

interface Book {
  title: string
  year?: number
}

export default defineComponent({
  name: 'HelloWorld',
  setup() {
    const book = reactive<Book>({ title: 'Vue 3 Guide' })
    // or
    const book: Book = reactive({ title: 'Vue 3 Guide' })
    // or
    const book = reactive({ title: 'Vue 3 Guide' }) as Book
  }
})

类型声明 computed

import { defineComponent, ref, computed } from 'vue'

export default defineComponent({
  name: 'CounterButton',
  setup() {
    let count = ref(0)

    // 只读
    const doubleCount = computed(() => count.value * 2)

    const result = doubleCount.value.split('') 
    // => Property 'split' does not exist on type 'number'
  }
})

事件绑定e.target需要声明类型

<template>
  <input type="text" @change="handleChange" />
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  setup() {
    // `evt` 将会是 `any` 类型
    const handleChange = evt => {
      console.log(evt.target.value) // 此处 TS 将抛出异常
    }
    return { handleChange }
  }
})
</script>

如你所见,在没有为 evt 参数正确地声明类型的情况下,当我们尝试获取 元素的值时,TypeScript 将抛出异常。解决方案是将事件的目标转换为正确的类型:

const handleChange = (evt: Event) => {
  console.log((evt.target as HTMLInputElement).value)
}