本文總結一下TypeScript類型聲明的書寫,不少時候寫TypeScript不是問題,寫類型就特別糾結,我總結下,我在使用TypeScript中遇到的問題。若是你遇到類型聲明不會寫的時候,多看看
lodash
的聲明,由於lodash
對數據進行各類變形操做,因此你能遇到的,都有參考示例。css
// 變量
const num: number = 1;
const str: string = 'str';
const bool: boolean = true;
const nulls: null = null;
const undefine: undefined = undefined;
const symbols: symbol = Symbol('symbal');
const any: any = 'any types'; // typescript的any類型,至關於什麼類型約束都沒有
複製代碼
// 數組: 推薦使用T[]這種寫法
const nums: number[] = [1, 2, 3, 4];
// 不推薦:Array<T>泛型寫法,由於在JSX中不兼容,因此爲了統一都使用T[]這種類型
const strs: Array<string> = ['s', 't', 'r'];
const dates: Date[] = [new Date(), new Date()];
複製代碼
// 數組concat方法的never問題
// 提示: Type 'string' is not assignable to type 'never'.
const arrNever: string[] = [].concat(['s']);
// 主要問題是:[]數組,ts沒法根據上下文判斷數組內部元素的類型
// @see https://github.com/Microsoft/TypeScript/issues/10479
const fixArrNever: string[] = ([] as string[]).concat(['s']);
複製代碼
接口是 TypeScript 的一個核心知識,它能合併衆多類型聲明至一個類型聲明:html
並且接口能夠用來聲明:函數,類,對象等數據類型前端
interface Name {
first: string;
second: string;
}
let username: Name = {
first: 'John',
second: 'Doe'
};
複製代碼
// 特殊類型
const any: any = 'any types'; // typescript的any類型,至關於什麼類型都沒寫
let nobody: any = 'nobody, but you';
nobody = 123;
let nulls: number = null;
let bool: boolean = undefined;
// void
function printUsername (name: string): void {
console.log(name);
}
複製代碼
聯合類型在
option bags
模式場景很是實用,使用 **| **來作標記vue
function options(opts: { types?: string; tag: string | number; }): void {
}
複製代碼
最典型的使用場景就是繼承和mixin,或者copy等操做node
// 交叉類型:若是之後遇到此種類型聲明不會寫,直接看Object.assign聲明寫法
function $extend<T, U>(first: T, second: U): T & U {
return Object.assign(first, second); // 示意而已
}
複製代碼
元組不多使用ios
let nameNumber: [string, number];
// Ok
nameNumber = ['Jenny', 221345];
// Error
// nameNumber = ['Jenny', '221345'];
let tuple: [string, number];
nameNumber = ['Jenny', 322134];
const [usernameStr, uselessNum] = nameNumber;
複製代碼
type用來建立新的類型,也能夠重命名(別名)已有的類型,建議使用type建立簡單類型,無嵌套的或者一層嵌套的類型,其它複雜的類型都應該使用interface, 結合implements ,extends實現。git
type StrOrNum = string | number;
// 使用
let sample: StrOrNum;
sample = 123;
sample = '123';
// 會檢查類型
sample = true; // Error
複製代碼
若是第三方庫沒有提供聲明文件,第一時間去微軟官方的倉庫https://github.com/borisyankov/DefinitelyTyped 查找,或者在
npmjs.com
上搜索@types/依賴的模塊名
大部分狀況均可以找到。github
手動添加聲明文件ajax
聲明文件通常都是統一放置在types文件夾下typescript
// 例子: types/axios.d.ts
declare module 'axios'; // 這裏的axios聲明爲any類型
複製代碼
例如一些庫直接把在
window
上添加的全局變量
// globals.d.ts
// 例子:jQuery,現實中jQuery是有.d.ts
declare const jQuery: any;
declare const $: typeof jQuery;
複製代碼
在前端工程中,import不少非js資源,例如:css, html, 圖片,vue, 這種ts沒法識別的資源時,就須要告訴ts,怎麼識別這些導入的資源的類型。
// 看看vue怎麼處理的:shims-vue.d.ts
declare module '*.vue' {
import Vue from 'vue';
export default Vue;
}
// html
declare module '*.html';
// css
declare module '*.css';
複製代碼
有時候遇到須要強制類型轉換,尤爲是對
聯合類型
或者可選屬性時。
// 第一種:使用<>括號
const convertArrType: string[] = <Array<string>>[].concat(['s']);
// 第二種:使用as關鍵字
const fixArrNever: string[] = ([] as string[]).concat(['s']);
複製代碼
建議使用第二種,由於兼容
JSX
,第一種官方也不推薦了,雖然它是合法的。
API中提供的參數不少都有默認值,或者屬性可選,怎麼書寫呢?
class Socket {}
// 聯合類型
export type SocketType = 'WebSocket' | 'SockJs';
export interface SocketOptions {
type: SocketType;
protocols?: string | string[]; // 可選
pingMessage: string | (() => string); // 聯合類型,能夠爲string或者函數 pongMessage: string | (() => string); } // 默認值 export function eventHandler = ( evt: CloseEvent | MessageEvent | Event, socket: Socket, type = 'WebSocket' // 默認值 ) => any;
複製代碼
剛開始我也很糾結這個問題,我就是一個獨立的函數,怎麼聲明類型呢?尤爲是寫事件處理函數的API時。
class Socket {}
// 函數的聲明方式
export type SocketEventHandler = (
evt: CloseEvent | MessageEvent | Event,
socket: Socket
) => any;
const eventHandler: SocketEventHandler = (evt, socket) => {
}
// 可選參數和rest參數
let baz = (x = 1) => {};
let foo = (x: number, y: number) => {};
let bar = (x?: number, y?: number) => {};
let bas = (...args: number[]) => {};
複製代碼
JavaScript中的對象均可以使用字符串索引直接取屬性或者調用方法,TypeScript中也有相應的類型聲明方法。
type Hello = {
hello: 'world';
// key只是一個形式屬性名(相似形參同樣)
[key: string]: string;
};
const greeting: Hello = {
hi: 'morning'
}
console.log(greeting['hi'])
複製代碼
有的時候咱們只聲明瞭一個基本的類型結構,而後後續有擴展的狀況 ,尤爲時二次封裝時的options。
interface AxiosOptions {}
type AjaxOptions = {
axiosOptions: AxiosOptions;
// 額外擴展的放入到單獨的屬性節點下
extraOptions: {
[prop: string]: any
};
};
type AjaxOptions1 = {
axiosOptions?: AxiosOptions;
// 不要這樣寫,由於axiosOptions拼寫錯誤時,ts不會提示
// 儘可能把後續擴展的屬性,移動到獨立的屬性節點下
[prop: string]: any
};
const ajaxOptions: AjaxOptions1 = {
axiosOptions1: {}; // 本意是axiosOptions,可是ts不會提示
}
複製代碼
! 標識符告訴ts編譯器,聲明的變量沒有問題,再運行期不會報錯。
class BaseSelect extends Vue {
options: string[]; // 這裏會提示沒有在constructor中初始化
created() {
this.options = ['inited']
}
}
class BaseSelect extends Vue {
options!: string[]; // 使用 ! 告訴編譯器,我知道本身在作什麼
created() {
this.options = ['inited']
}
}
複製代碼
對於獨立使用的函數,能夠聲明指定的調用上下文
class Handler {
info: string;
// 聲明指定的this上下文
onClickBad(this: Handler, e: Event) {
// oops, used this here. using this callback would crash at runtime
this.info = e.message;
}
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // error!
複製代碼
來看看使用場景,擴展vue,在vue上添加全局的屬性。
// vue的聲明在 vue/types/vue.d.ts
declare module 'vue/types/vue' {
// 至關於Vue.$eventBus
interface Vue {
$eventBus: Vue;
}
// 至關於在Vue.prototype.$eventBus
interface VueConstructor {
$eventBus: Vue;
}
}
複製代碼
Use // @ts-check to opt in to type checking for a single file.
Use // @ts-nocheck to opt out of type checking for a single file.
Use // @ts-ignore to opt out of type checking for a single line.
複製代碼
TypeScript
聲明還有不少高級的用法,目前我也沒有用到那麼多,在我糾結不會寫聲明的時候,我就會看看別人的聲明文件時怎麼寫的。
注意:儘可能不要把解構和聲明寫在一塊兒,可讀性極差。
class Node {
onNodeCheck(checkedKeys: any, { // 解構
checked, checkedNodes, node, event,
} : { // 聲明
node: any;
[key: string]: any;
}
) {
}
}
複製代碼
關注公衆號,發現更多精彩內容。