TypeScript入门(与VUE2集成)

2022-03-02 08:56:47 浏览数 (1)

安装

安装

代码语言:javascript复制
npm install -g typescript

打开项目文件夹

代码语言:javascript复制
tsc -init

编译

代码语言:javascript复制
tsc ts01.ts

监听编译

代码语言:javascript复制
tsc -w

监听编译某个文件

代码语言:javascript复制
tsc -w ts01.ts

修改编译生成JS的位置和目标语法

打开tsconfig.json,修改outDir的值,并解除注释

代码语言:javascript复制
{
  "compilerOptions": {
    "target": "es3",
    "outDir": "./js/"
  }
}

默认会转成ES6,这里建议转换为ES3或ES5来兼容大多数浏览器。

语法

数据类型

代码语言:javascript复制
let num: number = 1;
let str: string = "hello";
let b: boolean = true;
let n: null = null;
let un: undefined = undefined;
let f: any = 1;
// 获取类型
console.log("typeof(str):" typeof(str))
// 数组
let arr1: Array<number> = [1, 2, 3];
let arr2: number[] = [1, 2, 3];
let arr3: string[] = ["aa", "bb", "cc"];
let arr4: Array<number | string> = [1, "aa", 3];
// 元组
let tup: [string, string, number] = ['Dylan', 'male', 23];
console.log("typeof(tup):" typeof(tup))

数组和元组的区别?

元组可以理解为一个固定长度,每一项元素类型都确定的数组。

我们看一个示例

代码语言:javascript复制
let tup: [string, string, number] = ['Dylan', 'male', 23];
tup.push("123");
tup[3] = 123;

示例2

代码语言:javascript复制
let tup: [string, string, number] = ['Dylan', 'male', 23];
tup.pop();
tup[2] = 456;

这个示例中我们可以发现元组的几个问题:

  1. 虽然长度固定,但是我们可以push元素,使之长度超过定义的长度,不会报错。但是根据下标取值的时候不能超过定义时的长度。
  2. push超出长度,转换的js是能够正常运行的,并且打印结果也是包含超出长度的元素,所以不建议通过push添加元素,建议通过下标设置。
  3. push的时候数据类型可以是定义的时候所包含的类型,不能是其它类型。
  4. 根据下标赋值时类型必须和定义的时候一样。
  5. pop删除元素后,我们依旧可以通过下标赋值。

接口 类 接口的实现 类的集成 与 方法重载

代码语言:javascript复制
interface Person { 
  run():void;
}

class Men implements Person { 
  name: string;
  age: number;

  constructor(name:string,age:number) { 
    this.name = name;
    this.age = age;
  }
  run(): void {
    console.log("run");
  }

  talk(): void;
  talk(str:string): void;
  talk(num:number): void;
  talk(str:string,num:number): void;
  talk(par1?:any,par2?:any) {
    if (par1 == undefined && par2 == undefined) {
      console.log("method 1");
    }
    else if (par2 == undefined) {
      if (typeof par1 == "string") {
        console.log("method 2");
      }
      else if (typeof par1 == "number") {
        console.log("method 3");
      }
    } else { 
      console.log("method 4");
    }
  }
}

class SuperMen extends Men{

}

let men = new Men("小明", 18);
men.talk();
men.talk("小明");
men.talk(18);
men.talk("小明",18);

结果可以看到

代码语言:javascript复制
method 1
method 2
method 3
method 4

VUE2集成TS

新项目创建时可以直接选择ts,这里主要说旧项目升级的情况。

安装依赖

代码语言:javascript复制
npm i vue-class-component vue-property-decorator --save
npm i ts-loader typescript tslint tslint-loader tslint-config-standard -D

配置vue.config.js添加下面的代码

代码语言:javascript复制
module.exports = {
  configureWebpack: {
    resolve: { extensions: [".ts", ".tsx", ".js", ".json"] },
    module: {
      rules: [
        {
          test: /.ts$/,
          exclude: /node_modules/,
          enforce: 'pre',
          loader: 'tslint-loader'
        },
        {
          test: /.tsx?$/,
          loader: 'ts-loader',
          exclude: /node_modules/,
          options: {
            appendTsSuffixTo: [/.vue$/],
          }
        }
      ]
    }
  },
}

新建tsconfig.json放在项目根目录

代码语言:javascript复制
{
  "compilerOptions": {
    "allowJs": true,
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "webpack-env"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

在src根目录下添加两个TS文件

shims-tsx.d.ts

代码语言:javascript复制
// shims-tsx.d.ts  src目录下
import Vue, { VNode } from 'vue';
declare global {
  namespace JSX {
    // tslint:disable no-empty-interface
    interface Element extends VNode {}
    // tslint:disable no-empty-interface
    interface ElementClass extends Vue {}
    interface IntrinsicElements {
      [elem: string]: any;
    }
  }
}

新建shims-vue.d.ts

代码语言:javascript复制
// shims-vue.d.ts   src目录下
declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}

//为了TypeScript做的适配定义文件,因为.vue文件不是一个常规的文件类型,
//TypeScript是不能理解vue文件是干嘛的,加这一段是是告诉 TypeScript,vue文件是这种类型的。
//没有这个文件,会发现 import 导入的所有.vue类型的文件都会报错。
//axios报错
declare module 'axios' {
  interface AxiosInstance {
    (config: AxiosRequestConfig): Promise<any>
  }
}

添加tslint.json

代码语言:javascript复制
{
  "extends": "tslint-config-standard",
  "globals": {
    "require": true
  }
}

main.js改成main.ts配置vue.config.js的入口为main.ts

代码语言:javascript复制
pages: {
    index: {
      entry: 'src/main.ts',
    }
  },

安装@typescript-eslint/parser

将eslint校验改成@typescript-eslint/parser

代码语言:javascript复制
npm install @typescript-eslint/parser --save

更改.eslintrc.js

代码语言:javascript复制
parserOptions: {
  parser: '@typescript-eslint/parser'
},

代码格式

新旧写法对比

原写法

代码语言:javascript复制
<script>
  export default {
    name: 'xx',// 组件名
    components: {},// 组件
    props: {},// 父组件传递来的值
    data() { // 属性
      return {};
    },
    computed:{}, // 计算属性
    watch:{},// 监听器
    mounted() {}, // 生命周期钩子函数
    methods: {} // 方法
  };
</script>

新写法(TS)

代码语言:javascript复制
<template>
  <div class="home">
    <HelloWorld :msg="message" />
    {{ computedMsg }}
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue, Watch, Emit } from "vue-property-decorator";
import HelloWorld from "@/components/HelloWorld.vue";

@Component({
  components: {
    HelloWorld,
  },
})
export default class Home extends Vue {
  // props
  @Prop({
    type: Number,
    default: 1,
    required: false,
  })
  propA?: number;

  @Prop()
  propB?: string;

  // data
  public message = "你好 TS";

  // computed
  public get computedMsg(): string {
    return "这里是计算属性"   this.message;
  }

  // watch属性
  @Watch("propA", {
    deep: true,
  })
  public test(newValue: string, oldValue: string): void {
    console.log("propA值改变了"   newValue   " oldValue:"   oldValue);
  }

  // $emit
  // 原来写法:this.$emit('mysend',this.message)
  // 现在直接写 this.bindSend()
  @Emit("mysend")
  bindSend(): string {
    return this.message;
  }

  // mounted 生命周期钩子函数
  mounted(): void {
    console.info("mounted");
    this.mymethod();
  }

  // methods中的方法
  public mymethod(): void {
    this.bindSend();
  }
}
</script>

$emit

对比

代码语言:javascript复制
count = 0
// this.count  = n; this.$emit('add-to-count', n)
@Emit("add-to-count")
addToCount(n: number) {
  this.count  = n;
}

// this.count = 0; this.$emit('reset');
@Emit("reset")
resetCount() {
  this.count = 0;
}

// this.$emit('return-value', 10)
@Emit('return-value')
returnValue() {
  return 10;
}

// 相当于 this.$emit('on-input-change', num 1, num)
@Emit("on-input-change")
onInputChange(num: number) {
  return num   1;
}

// 相当于
// const promise = new Promise((resolve) => {
//   setTimeout(() => {
//     resolve(20)
//   }, 0)
// })
//
// promise.then((value) => {
//   this.$emit('promise', value)
// })
@Emit()
promise() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(20);
    }, 0);
  });
}

从上面的示例可以看出

方法名就是$emit的第一个参数,除非设置了别名。 方法的返回值为$emit第二个参数。 方法的传参是$emit的第三个参数,如果方法没有返回值,则为第二个参数。 执行顺序为先执行方法体内的代码,再$emit

0 人点赞