Vue+Typescript起手式

2,476 阅读7分钟

TypeScript是什么?

TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,TypeScript 在 JavaScript 的基础上添加了可选的静态类型和基于类的面向对象编程。

其实TypeScript就是相当于JavaScript的增强版,但是最后运行时还要编译成JavaScript。TypeScript最大的目的是让程序员更具创造性,提高生产力,它将极大增强JavaScript编写应用的开发和调试环节,解决JavaScript的“痛点”:弱类型和没有命名空间,导致很难模块化,不适合开发大型程序。

另外它还提供了一些语法糖来帮助大家更方便地实践面向对象的编程。强类型还有一个最大好处就是智能提示,例如你可以知道当前变量具有哪些属性和方法。

传统的一般在注释里面注明入参出参,在写业务的时候编辑器也不会智能提示,比如:

加入TypeScript后:如果我在定义变量的时候故意不按定义时的强类型写呢:

TypeScript语法


参数类型

Typescript中的参数类型包括:boolean/number/string/array/tuple/enum/any/(null和undefined)/ void /never。


String 类型

一个保存字符串的文本,类型声明为 string。可以发现类型声明可大写也可小写,后文同理。

let name: string = 'sun'let name2: string = 'jun'


Boolen 类型

boolean是 true 或 false 的值

let isBool1: boolean = false


Number 类型

let number: number = 10


Array 类型

数组是 Array 类型。然而,因为数组是一个集合,我们还需要指定在数组中的元素的类型。我们通过 Array<type> or type[] 语法为数组内的元素指定类型

let arr:number[] = [1, 2, 3, 4, 5];
let arr2:Array<number> = [1, 2, 3, 4, 5]; // 数组泛型
let arr3:string[] = [“1”,”2”];
let arr4:Array<string> = ["1","2"];
interface NumberArray { 
    [index:number]: number
}
let fibonacci: NumberArray = [1,2,3,4]


Enums 类型

列出所有可用值,一个枚举的默认初始值是0。你可以调整一开始的范围:

enum REN { nan, nv, yao }console.log(REN.yao)  //返回了2,这是索引index,跟数组很像。
enum REN {    
    nan = '男',    
    nv = '女',    
    yao= '妖'
}
console.log(REN.yao)  //返回了妖 这个字


元组 Tuple

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同(数组合并了相同类型的对象,而元组合并了不同类型的对象)

let tuple: [number,string,string]
tuple = ['sun',18,'jun'] // err 
// 如果不遵循 为元组 预设排序的索引规则,那么Typescript会警告。


Any 类型

any 是默认的类型,其类型的变量允许任何类型的值:

let notSure: any = 10
let notSure2: any[] = [1,"2",false]


Void 类型

JavaScript 没有空值 Void 的概念,在 TypeScirpt 中,可以用 void 表示没有任何返回值的函数:

function alertName(): void {  
    console.log('My name is sunjun')
}


联合类型

let name: string | number = 'sunjun'// 连接符 |
name = 1


never类型

never类型表示的是那些永不存在的值的类型。例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型;变量也可能是 never类型,当它们被永不为真的类型保护所约束时。

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {    
    throw new Error(message);
}
// 推断的返回值类型为never
function fail() {    
    return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {    
    while (true) {    }
}


泛型

function Hello<T>(arg:T):T {    
    return arg
}
let outPut = Hello<string>('Hello')
let output2 = Hello('Hello')
let output3 = Hello(123)
console.log(outPut)
console.log(outPut2)
console.log(outPut3)

我们给 Hello 函数添加了类型变量 T ,T 帮助我们捕获用户传入的类型(比如:string)。Hello 函数叫做泛型,因为它可以适用于多个类型。代码中 output 和 output2 是效果是相同的,第二种方法更加普遍,利用了类型推论 —— 即编译器会根据传入的参数自动地帮助我们确定T的类型


类型断言

有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息。通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。

通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。它没有运行时的影响,只是在编译阶段起作用。TypeScript会假设你,程序员,已经进行了必须的检查。

类型断言有两种形式。其一是“尖括号”语法:

let someValue: any = "this is a string"
let strLength: number = (<string>someValue).length

另一个为as语法

let someValue: any = "this is a string"
let strLength: number = (someValue as string).length

注意:两种形式是等价的。至于使用哪个大多数情况下是凭个人喜好;然而,当你在TypeScript里使用JSX时,只有 as语法断言是被允许的。


接口

是一种规范, 通常用来定义规范(或数据结构), 必须遵守该规范。

interface User {  
    name: string  
    age: number
}
interface Name {   
    name: string
}
interface User extends Name {   
    age: number
}
let user:User = { 
    name:'sunjun', 
    age:18 
}
console.log(user)


声明文件declare

//列举出几个常用的:
declare var 声明全局变量
declare function 声明全局方法
declare class 声明全局类
declare enum 声明全局枚举类型
declare global 扩展全局变量
declare module 扩展模块


可选参数 ( ?: )和非空断言操作符(!.)

function buildName(a: string, b?: string) {    
    return a + ' ' + b
}
//b?代表参数b可传可不传。
// 错误演示
buildName("a", "b", "c")
// 正确演示
buildName("a")
// 正确演示
buildName("a", "b")
let s = e!.name  // 断言e是非空并能访问name属性,能确定变量值一定不为空时使用。


vue+ts 起手式

vue create my-app



自定义细节配置

Use class-style component syntax? (Y/n):是否使用class风格的组件语法 Y

Use Babel alongside TypeScript for auto-detected polyfills? (Y/n) :是否使用babel做转义 Y

Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n):是否使用history模式

Pick a linter / formatter config:选择一个linter

Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default):选择预处理的模式

Pick additional lint features:选择 保存时检查 / 提交时检查

Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? (Use arrow keys):选择配置信息存放位置,单独存放或者并入package.json

Save this as a preset for future projects? (y/N) :是否保存当前预设,下次构建无需再次配置

// shims-tsx.d.ts, 在全局变量 global中批量命名了数个内部模块。
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,意思是告诉 TypeScript *.vue 后缀的文件可以交给 vue 模块来处理。
declare module '*.vue' {  
    import Vue from 'vue';  
    export default Vue;
}
// declare:当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。


Vue组件的Ts写法

@Emit 指定事件 emit,可以使用此修饰符,也可以直接使用 this.$emit()

@Inject 指定依赖注入

@Mixins mixin 注入

@Model 指定 model

@Prop 指定 Prop

@Provide 指定 Provide

@Watch 指定 Watch

@Component export from vue-class-component

具体的装饰器语法见:https://github.com/kaorun343/vue-property-decorator#PropSync

举个例子

import {  Component, Prop, Watch, Vue } from 'vue-property-decorator'
@Component
export class MyComponent extends Vue {   
    dataA: string = 'test'   
    @Prop({ default: 0 })   
    propA: number   
    // watcher   
    @Watch('child')   
    onChildChanged (val: string, oldVal: string) {}   
    @Watch('person', { immediate: true, deep: true })   
    onPersonChanged (val: Person, oldVal: Person) {}   
    // 其他修饰符详情见上面的 github 地址,这里就不一一做说明了
}

解析成我们普通的VUE

export default {  
    data () {    
        return {       
            dataA: 'test'    
        }  
    },  
    props: {     
        propA: {       
            type: Number,        
            default: 0     
        }  
    },  
    watch: {    
        'child': {        
            handler: 'onChildChanged',        
            immediate: false,        
            deep: false    
        },    
        'person': {        
            handler: 'onPersonChanged',        
            immediate: true,        
            deep: true    
        }  
    },  
    methods: {      
        onChildChanged (val, oldVal) {},      
        onPersonChanged (val, oldVal) {}  
    }
}


vuex-class

vuex-class 是一个基于 Vue、Vuex、vue-class-component 的库,和 vue-property-decorator 一样,它也提供了4 个修饰符以及 namespace,解决了 vuex 在 .vue 文件中使用上的不便的问题。

@State @Getter @Mutation @Action namespace

import Vue from 'vue'import Component from 'vue-class-component'
import {  State,  Getter,  Action,  Mutation,  namespace } from 'vuex-class'
const someModule = namespace('path/to/module')
@Component
export class MyComp extends Vue {  
    @State('foo') stateFoo  
    @State(state => state.bar) stateBar  
    @Getter('foo') getterFoo  
    @Action('foo') actionFoo  
    @Mutation('foo') mutationFoo  
    @someModule.Getter('foo') moduleGetterFoo  
    created () {        
        this.stateFoo // -> store.state.foo    
        this.stateBar // -> store.state.bar    
        this.getterFoo // -> store.getters.foo    
        this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })    
        this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true })    
        this.moduleGetterFoo // -> store.getters['path/to/module/foo']  
    }
}

到这里,ts 在 .vue 文件中的用法介绍的也差不多了。我也相信小伙伴看到这,对其大致的语法糖也有了一定的了解了