新手學習 TyptScript 入門(基於vue組件轉換說明)

1. 基本類型合集

let decLiteral:number = 6;  // 數字,2、8、十六進制都支持
let hexLiteral:number = 0xf00d;
let name:string = 'bob'; // 字符串,單雙引都行
let sentence:string = `Hello, my name is ${ name }`

// 數組,第二種方式是使用數組泛型,Array<元素類型>:
let list:number[] = [1,2,3];
let list:Array<number> = [1,2,3]
let u:undefined = undefined;
let n:null = null;
Typescript與Javascript共享相同的基本類型,但有一些額外的類型:
①元組 Tuple
②枚舉 enum
③Any 與Void


【2. 特殊類型】
【A.元組 Tuple】
想象 元組 做爲有組織的數組,你須要以正確的順序預約義數據類型。
const messyArray = ['something', 2, true, undefined, null]
const tuple: [number, string, string] = [24, "Indrek" , "Lasn"]
若是不遵循 爲元組 預設排序的索引規則,那麼Typescript會警告。


【B.枚舉 enum】
enum類型是對JavaScript標準數據類型的一個補充。 像C#等其它語言同樣,使用枚舉類型能夠爲一組數值賦予友好的名字。
// 默認狀況從0開始爲元素編號,也可手動爲1開始
enum Color{ Red=1,Green=2,Blue=4 }
let c:Color = Color.Green;
let colorName:string = Color[2];
console.log(colorName); // 輸出'Green'由於上面代碼裏它的值是2


【C.Void】
在Typescript中,你必須在函數中定義返回類型。像這樣:
function sayMyName(name: string): string{
    return name
}
console.log(sayMyName("indrek"));   // 輸出結果是 indrek

若沒有返回值,則會報錯:
咱們能夠將其返回值sayMyName定義爲void,則會報錯:
function sayMyName(name: string): void{
    return name
}


【D. Any】
就是什麼類型都行,當你沒法確認在處理什麼類型時能夠用這個。
但要慎重使用,用多了就失去使用Ts的意義。
let person:any = '機器人';
person = 24;
person = true;
主要應用場景有:1.接入第三方庫   2.Ts菜逼前期都用


【E. Never】
用很粗淺的話來描述就是:"Never是你永遠得不到的爸爸。"
具體的行爲是:
① throw new Error(message)
② return error("Something failed")
③ while (true) {} // 存在沒法達到的終點

const error = (message: string): never => {
    throw new Error(message)
}
const showError = () => error('generic error message');
showError()
【3. 類型斷言】
簡略的定義是:能夠用來手動指定一個值的類型。
有兩種寫法,尖括號和as:
let someValue:any = "this is a string";
let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;

使用例子有:
當 TypeScript 不肯定一個聯合類型的變量究竟是哪一個類型的時候,咱們只能訪問此聯合類型的全部類型裏共有的屬性或方法.
例如:
function getLength(something: string | number):number{
    return something.length
}
報錯:
// index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
// Property 'length' does not exist on type 'number'.
若是你訪問長度將會報錯,而有時候,咱們確實須要在還不肯定類型的時候就訪問其中一個類型的屬性或方法,此時須要斷言纔不會報錯:
修改成:
function getLength(something: string | number):number{
    if((<string>something).length){
        return((<string>something).length)
    } else {
        return something.toString().length
    }
}
【4. 泛型:Generics】
軟件工程的一個主要部分就是構建組件,構建的組件不只須要具備明確的定義和統一的接口,同時也須要組件可複用。支持現有的數據類型和未來添加的數據類型的組件爲大型軟件系統的開發過程提供很好的靈活性。
在C#和Java中,可使用"泛型"來建立可複用的組件,而且組件可支持多種數據類型。這樣即可以讓用戶根據本身的數據類型來使用組件。


【A. 泛型方法】
在TypeScript裏,聲明泛型方法有如下兩種方式:
function gen_func1<T>(arg: T): T{
    return arg
}
// 或者
let gen_func2:<T>(arg: T) => T = function (arg){
    return arg;
}
調用方式也有兩種:
gen_func1<string>('Hello world');
gen_func2('Hello world');  // 第二種調用方式可省略類型參數,由於編譯器會根據傳入參數來自動識別對應的類型。


【B. 泛型與Any】
Ts 的特殊類型 Any 在具體使用時,能夠代替任意類型,咋一看二者好像沒啥區別,其實否則:
// 方法一: 帶有any參數的方法
functoin any_func(arg: any): any{
    return arg;
}
// 方法二: Array泛型方法
function array_func<T>(arg: Array<T>): Array<T>{
    return arg;
}
方法一,打印了arg參數的length屬性。由於any能夠代替任意類型,因此該方法在傳入參數不是數組或者帶有length屬性對象時,會拋出異常。
方法二,定義了參數類型是Array的泛型類型,確定會有length屬性,因此不會拋出異常。


【C. 泛型類型】
泛型接口例子:
interface Generice_interface<T>{
    (arg: T): T
}
function func_demo<T>(arg: T): T{
    return arg;
}
let func1: Generice_interface<number> = func_demo;
func1(123); // 正確類型的實際參數
func1('123'); // 錯誤類型的實際參數
【5. 自定義類型:Interface vs Type alias】
Interface,國內翻譯成接口。
Type alias,類型別名。

Typescript 中的 interface 和 type 到底有什麼區別
    【1: 相同點】
        【A: 均可以用來描述一個對象或函數】
            interface User{
                name: string,
                age: number
            }
            type User = {
                name: string,
                age: number
            }
            interface setUser {
                (name: string, age: number):void
            }
            type setUser = (name: string, age:number):void
       【B: 都容許拓展(extends 繼承)】
             //// interface extends interface
             interface Name{
                 name: string;
             }
             interface User extends Name{
                 age:number
             }
             
             //// type extends type
             type Name = {
                 number:string
             }
             type User = Name & { age: number }
             
             //// interface extends type
             type Name = {
                 name: string
             }
             interface User extends Name {
                 age: number
             }
             
             //// type extends interface
             interface Name{
                 name: string
             }
             type User = Name & {
                 age: number
             }
             
             
  【2. 不一樣點】
      【A: type 能夠而 interface 不行】
          type 能夠聲明基本類型別名,聯合類型,元組等類型
          // 基本類型別名
          type Name = string
          // 聯合類型
          interface Dog{
              wong()
          }
          interface Cat{
              miao()
          }
          type Pet = Dog | Cat
          // 具體定義數據每一個位置的類型
          type PetList = [Dog, Pet]
          
     【B: interface能夠而 type不行】
         【一: interface 可以聲明合併】
              interface User{
                  name: string,
                  age: number
              }
              interface User{
                  sex: string
              }
              /*
                User 接口爲 {
                 name: string
                 age: number
                 sex: string 
                } */
         【二: interface 有可選屬性和只讀屬性】
              1. 可選屬性
                  接口裏的屬性不全都是必需的。 有些是隻在某些條件下存在,或者根本不存在。 
                  例如給函數傳入的參數對象中只有部分屬性賦值了。
                  帶有可選屬性的接口與普通的接口定義差很少,只是在可選屬性名字定義的後面加一個?符號。
                  以下所示:
                  interface Person{
                      name: string,
                      age?:number,
                      gender?:number
                  }
               2. 可讀屬性
                   顧名思義就是這個屬性是不可寫的,對象屬性只能在對象剛剛建立的時候修改其值。 
                   你能夠在屬性名前用 readonly來指定只讀屬性。
                   以下所示:
                   interface User{
                       readonly loginName: string;
                       password: string
                   }
                   上面的例子說明,當完成User對象的初始化後loginName就不能夠修改了。
          
     【C: type 語句中還可使用 typeof獲取實例的 類型進行賦值】
          // 當你想獲取一個變量的類型時,使用typeof
          let div = document.createElement("div")
          type B = typeof div
          
          其餘騷操做
          type StringOrNumber = string | number;
          type Text = string | { text:string };
          type NameLookup = Dictionary<string, Persion>;
          type Callback<T> = (data: T) => void;
          type Pair<T> = [T,T];
          type Coordinates = Pair<number>;
          type Tree<T> = T | { left:Tree<T>, right:Tree<T> };
【6. 實現與繼承:implementsvsextends】
extends很明顯就是ES6裏面的類繼承,那麼implement又是作什麼的呢?它和extends有什麼不一樣?
implement,實現。與C#或Java裏接口的基本做用同樣,TypeScript也可以用它來明確的強制一個類去符合某種契約
implement基本用法:
    interface IDeveloper{
        name: string,
        age?:number
    }
    // 正確用法一
    class dev implements IDeveloper{
        name = "Alex",
        age = 20
    }
    // 正確用法二
    class dev2 implements IDeveloper{
        name = "Alex"
    }
    // 錯誤
    class dev3 implements IDeveloper{
        name = "Alex",
        age = "23"
    }
而extends是繼承父類,二者其實能夠混着用:  class A extends B implements C,D,E
示例:
    interface Shape{
        area(): number
    }
    type Perimenter = {
        perimiter(): number
    }
    
    class Rectangle implements PointType, Perimenter, Shape{
        x = 2,
        y = 3,
        area(){
            return this.x * this.y
        }
        perimiter(){
            return 2 * (this.x + this.y)
        }
    }
【7. 聲明文件與命名空間:declare 和 namespace】
前面咱們講到Vue項目中的shims-tsx.d.ts和shims-vue.d.ts,其初始內容是這樣的:
// shims-tsx.d.ts
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
declare module '*.vue' {
    import Vue from 'vue';
    export default Vue;
}

declare:當使用第三方庫時,咱們須要引用它的聲明文件,才能得到對應的代碼補全、接口提示等功能。
這裏列舉出幾個經常使用的:
    declare var 聲明全局變量
    declare function 聲明全局方法
    declare class 聲明全局類
    declare enum 聲明全局枚舉類型
    declare global 擴展全局變量
    declare module 擴展模塊
    
namespace:「內部模塊」如今稱作「命名空間」
module X (至關於如今推薦的寫法 namespace X)
跟其餘 JS 庫協同
相似模塊,一樣也能夠經過爲其餘 JS 庫使用了命名空間的庫建立 .d.ts 文件的聲明文件。
如爲 D3 JS 庫,能夠建立這樣的聲明文件:
    declare namespace D3{
        export interface Selectors{...}
    }
    declare var d3: D3.Base;
因此上述兩個文件:
    shims-tsx.d.ts, 在全局變量 global中批量命名了數個內部模塊。
    shims-vue.d.ts,意思是告訴 TypeScript *.vue 後綴的文件能夠交給 vue 模塊來處理。
【8. 訪問修飾符:private(公有)、public(私有)、protected(子女)】
其實很好理解:

1.  默認爲public(公有)

2.  當成員被標記爲private時,它就不能在聲明它的類的外部訪問,好比: 
    class Animal {
     private name: string;
     constructor(theName: string) {
         this.name = theName;
     }
    }
    let a = new Animal('Cat').name; //錯誤,‘name’是私有的

3. protected和private相似,可是,protected成員在派生類中能夠訪問
   class Animal {
     protected name: string;
     constructor(theName: string) {
         this.name = theName;
     }
   }
   class Rhino extends Animal {
     constructor() {
         super('Rhino');
     } 
     getName() {
         console.log(this.name) //此處的name就是Animal類中的name
     }
   }
【9. 可選參數 ( ?: )和非空斷言操做符(!.)】
可選參數: 
    function buildName(firstName: string, lastName ?: string){
        return firstName + " " + lastName
    }
    // 錯誤演示
    buildName("firstName", "lastName", "lastName")
    buildName("firstName", true)
    // 正確演示
    buildName("firstName")
    // 正確演示
    buildName("firstName", "lastName")
非空斷言操做符:
能肯定變量值必定不爲空時使用。
與可選參數 不一樣的是,非空斷言操做符不會防止出現 null 或 undefined。
let s = e!.name; // 斷言e是非空並訪問name屬性
【10. Vue組件的Ts寫法】
從 vue2.5 以後,vue 對 ts 有更好的支持。根據官方文檔,vue 結合 typescript ,有兩種書寫方式:
** Vue.extend **
    import Vue from 'vue'
    const Component = Vue.extend({
        // type inference enabled
    })
** vue-class-component **
    import { Component, Vue, Prop } from 'vue-property-decorator'
    @Component
    export default class Test extends Vue{
        @Prop({ type: Object })
        private test: { value: string }
    }
理想狀況下,Vue.extend 的書寫方式,是學習成本最低的。在現有寫法的基礎上,幾乎 0 成本的遷移。
可是Vue.extend模式,須要與mixins 結合使用。在 mixin 中定義的方法,不會被 typescript 識別到
,這就意味着會出現丟失代碼提示、類型檢查、編譯報錯等問題。
【11.vue-class-component】
咱們回到src/components/HelloWorld.vue
    <template>
         <div class="hello">
         <h1>{{ msg }}</h1>
         </div>
    </template>
    <script lang="ts">
        import { Component, Prop, Vue } from 'vue-property-decorator';
        @Component
        export default class HelloWorld extends Vue {
             @Prop() private msg!: string;
        }
    </script>
有寫過python的同窗應該會發現似曾相識:
vue-property-decorator這個官方支持的庫裏,提供了函數 **裝飾器(修飾符)**語法
    
    【1. 函數修飾符 @】
        @」,與其說是修飾函數倒不如說是引用、調用它修飾的函數。
        或者用句大白話描述:@: "下面的被我包圍了。"
        舉個例子,下面的一段代碼,裏面兩個函數,沒有被調用,也會有輸出結果:
        test(f){
            console.log("before...")
            f()
            console.log("after...")
        }
        @test
        func(){
            console.log("func was called")
        }
        直接運行,輸出結果:
        before ...
        func was called
        after ...
        
        上面代碼能夠看出來:
        ① 只定義了兩個函數:test和func,沒有調用它們。
        ② 若是沒有「@test」,運行應該是沒有任何輸出的。
        可是,解釋器讀到函數修飾符「@」的時候,後面步驟會是這樣:
        ① 去調用test函數,test函數的入口參數就是那個叫「func」的函數;
        ② test函數被執行,入口參數的(也就是func函數)會被調用(執行);
        換言之,修飾符帶的那個函數的入口參數,就是下面的那個整個的函數。有點兒相似JavaScript裏面的 function a (function () { ... });
    
    【2. vue-property-decorator和vuex-class提供的裝飾器】
        vue-property-decorator的裝飾器:
        @Prop(父子組件之間傳值)
        @PropSync
        @Model(數據雙向綁定)
        @Watch(監聽數據變化)
        @Provide(提供)
        @Inject(注入)
        @Emit(子組件向父組件傳遞)
        @Component (provided by vue-class-component)
        Mixins(公用塊)
        vuex-class的裝飾器:
        @State(state)
        @Getter(getter)
        @Action(action)
        @Mutation(mutation)
        咱們拿原始Vue組件模版來看:
        import {componentA,componentB} from '@/components';
        export default {
         components: { componentA, componentB},
         props: {
             propA: { type: Number },
             propB: { default: 'default value' },
             propC: { type: [String, Boolean] } 
         }
         // 組件數據
         data () {
             return {
                 message: 'Hello'
             }
         },
         // 計算屬性
         computed: {
             reversedMessage () {
                 return this.message.split('').reverse().join('')
             }
             // Vuex數據
             step() {
                 return this.$store.state.count
             }
         },
         methods: {
             changeMessage () {
                 this.message = "Good bye"
             },
             getName() {
                 let name = this.$store.getters['person/name']
                 return name
             }
         },
         // 生命週期
         created () { },
         mounted () { },
         updated () { },
         destroyed () { }
        }
        以上模版替換成修飾符寫法則是:
        import { Component, Vue, Prop } from 'vue-property-decorator'
        import { State, Getter } from "vuex-class"
        import { count, name } from "@/person"
        import { componentA, componentB } from "@/components"
        @Component({
            components: { componentA, componentB  }
        })
        export default class HelloWord extends Vue{
            @Prop(Number) readonly propA!: number | undefined
            @Prop({default: "default value"}) readonly propB!:string
            @Prop([String, Boolean]) readonly propC!: string | boolean | undefined
            // 原data
            message = "Hello"
            // 計算屬性
            private get reversedMessage (): string[]{
                return this.message.split('').reverse().join('')
            }
            // Vuex數據
            @State ((state: IPootState) => state.booking.currentStep) step!:number
            @Getter("person/name") name!:name
            //methods
            public changeMessage():void{
                this.message = "Good bye"
            }
            public getName(): string{
                 let storeName = name
                 return storeName
            }
            // 生命週期
            private created():void{},
            private mounted():void{},
            private updated():void{},
            private destroyed():void{}
        }
        正如你所看到的,咱們在生命週期 列表那都添加private XXXX方法,由於這不該該公開給其餘組件。
        而不對method作私有約束的緣由是,可能會用到@Emit來向父組件傳遞信息。
【12.添加全局工具】
引入全局模塊,須要改main.ts:
    import Vue from 'vue';
    import App from './App.vue';
    import router from './router';
    import store from './store';
    Vue.config.productionTip = false;
    new Vue({
     router,
     store,
     render: (h) => h(App),
    }).$mount('#app');
    
    npm i VueI18n
    
    import Vue from 'vue';
    import App from './App.vue';
    import router from './router';
    import store from './store';
    // 新模塊
    import i18n from './i18n';
    Vue.config.productionTip = false;
    new Vue({
     router, 
     store, 
     i18n, // 新模塊
     render: (h) => h(App),
    }).$mount('#app');
    
    但僅僅這樣,還不夠。你須要動src/vue-shim.d.ts:
    // 聲明全局方法
    declare module 'vue/types/vue' {
     interface Vue {
         readonly $i18n: VueI18Next;
         $t: TranslationFunction;
     }
    }
    以後使用this.$i18n()的話就不會報錯了。
【13.Axios 使用與封裝】
若是隻是想簡單在Ts裏體驗使用Axios,能夠安裝vue-axios 簡單使用Axios
$ npm i axios vue-axios
main.ts添加:
    import Vue from 'vue'
    import axios from 'axios'
    import VueAxios from 'vue-axios'
    Vue.use(VueAxios, axios)
    而後在組件內使用:
    Vue.axios.get(api).then((response) => {
     console.log(response.data)
    })
    this.axios.get(api).then((response) => {
     console.log(response.data)
    })
    this.$http.get(api).then((response) => {
     console.log(response.data)
    })
    
1. 新建文件request.ts
文件目錄:
    -api
     - main.ts // 實際調用
    -utils
     - request.ts // 接口封裝
     
2. request.ts文件解析
    import * as axios from 'axios';
    import store from '@/store';
    // 這裏可根據具體使用的UI組件庫進行替換
    import { Toast } from 'vant';
    import { AxiosResponse, AxiosRequestConfig } from 'axios';
     /* baseURL 按實際項目來定義 */
    const baseURL = process.env.VUE_APP_URL;
     /* 建立axios實例 */
    const service = axios.default.create({
     baseURL,
     timeout: 0, // 請求超時時間
     maxContentLength: 4000,
    });
    service.interceptors.request.use((config: AxiosRequestConfig) => {
     return config;
    }, (error: any) => {
     Promise.reject(error);
    });
    service.interceptors.response.use(
     (response: AxiosResponse) => {
     if (response.status !== 200) {
     Toast.fail('請求錯誤!');
     } else {
     return response.data;
     }
     },
     (error: any) => {
     return Promise.reject(error);
     });
    export default service;
    
    爲了方便,咱們還須要定義一套固定的 axios 返回的格式,新建ajax.ts:
    export interface AjaxResponse {
     code: number;
     data: any;
     message: string;
   }
   
3. api.ts接口調用:
    // api/api.ts
    import request from '../utils/request';
    // get
    export function getSomeThings(params:any) {
     return request({
         url: '/api/getSomethings',
     });
    }
    // post
    export function postSomeThings(params:any) {
     return request({
         url: '/api/postSomethings',
         methods: 'post',
         data: params
     });
    }
相關文章
相關標籤/搜索