在 2020 年的今天,TS 已經愈來愈火,不論是服務端(Node.js),仍是前端框架(Angular、Vue3),都有愈來愈多的項目使用 TS 開發,做爲前端程序員,TS 已經成爲一項必不可少的技能,本文旨在介紹 TS 中的一些高級技巧,提升你們對這門語言更深層次的認知。javascript
Typescript = Type + ECMAScript + Babel-Litecss
Typescript 設計目標: github.com/Microsoft/T…html
any
和 unknown
的區別any
: 任意類型unknown
: 未知的類型任何類型都能分配給 unknown
,但 unknown
不能分配給其餘基本類型,而 any
啥都能分配和被分配。前端
let foo: unknown
foo = true // ok
foo = 123 //ok
foo.toFixed(2) // error
let foo1: string = foo // error
複製代碼
let bar: any
bar = true // ok
bar = 123 //ok
foo.toFixed(2) // ok
let bar1:string = bar // ok
複製代碼
能夠看到,用了 any
就至關於徹底丟失了類型檢查,因此你們儘可能少用 any
,對於未知類型能夠用 unknown
。vue
咱們能夠經過不一樣的方式將 unknown
類型縮小爲更具體的類型範圍:java
function getLen(value: unknown): number {
if (typeof value === 'string') {
// 由於類型保護的緣由,此處 value 被判斷爲 string 類型
return value.length
}
return 0
}
複製代碼
這個過程叫類型收窄(type narrowing)。node
never
通常表示哪些用戶沒法達到的類型。在最新的 typescript 3.7 中,下面代碼會報錯:react
// never 用戶控制流分析
function neverReach (): never {
throw new Error('an error')
}
const x = 2
neverReach()
x.toFixed(2) // x is unreachable
複製代碼
never
還能夠用於聯合類型的 幺元:webpack
type T0 = string | number | never // T0 is string | number
複製代碼
function fn(): number {
return 1
}
const fn = function (): number {
return 1
}
const fn = (): number => {
return 1
}
const obj = {
fn (): number {
return 1
}
}
複製代碼
在
()
後面添加返回值類型便可。c++
ts 中也有函數類型,用來描述一個函數:
type FnType = (x: number, y: number) => number
複製代碼
let myAdd: (x: number, y: number) => number = function(x: number, y: number): number {
return x + y
}
// 使用 FnType 類型
let myAdd: FnType = function(x: number, y: number): number {
return x + y
}
// ts 自動推導參數類型
let myAdd: FnType = function(x, y) {
return x + y
}
複製代碼
js由於是動態類型,自己不須要支持重載,ts爲了保證類型安全,支持了函數簽名的類型重載。即:
多個重載簽名
和一個實現簽名
// 重載簽名(函數類型定義)
function toString(x: string): string;
function toString(x: number): string;
// 實現簽名(函數體具體實現)
function toString(x: string | number) {
return String(x)
}
let a = toString('hello') // ok
let b = toString(2) // ok
let c = toString(true) // error
複製代碼
若是定義了重載簽名
,則實現簽名
對外不可見
function toString(x: string): string;
function toString(x: number): string {
return String(x)
}
len(2) // error
複製代碼
實現簽名
必須兼容重載簽名
function toString(x: string): string;
function toString(x: number): string; // error
// 函數實現
function toString(x: string) {
return String(x)
}
複製代碼
重載簽名
的類型不會合並
// 重載簽名(函數類型定義)
function toString(x: string): string;
function toString(x: number): string;
// 實現簽名(函數體具體實現)
function toString(x: string | number) {
return String(x)
}
function stringOrNumber(x): string | number {
return x ? '' : 0
}
// input 是 string 和 number 的聯合類型
// 即 string | number
const input = stringOrNumber(1)
toString('hello') // ok
toString(2) // ok
toString(input) // error
複製代碼
ts 中的類型推斷是很是強大,並且其內部實現也是很是複雜的。
基本類型推斷:
// ts 推導出 x 是 number 類型
let x = 10
複製代碼
對象類型推斷:
// ts 推斷出 myObj 的類型:myObj: { x: number; y: string; z: boolean; }
const myObj = {
x: 1,
y: '2',
z: true
}
複製代碼
函數類型推斷:
// ts 推導出函數返回值是 number 類型
function len (str: string) {
return str.length
}
複製代碼
上下文類型推斷:
// ts 推導出 event 是 ProgressEvent 類型
const xhr = new XMLHttpRequest()
xhr.onload = function (event) {}
複製代碼
因此有時候對於一些簡單的類型能夠不用手動聲明其類型,讓 ts 本身去推斷。
typescript 的子類型是基於 結構子類型
的,只要結構能夠兼容,就是子類型。(Duck Type)
class Point {
x: number
}
function getPointX(point: Point) {
return point.x
}
class Point2 {
x: number
}
let point2 = new Point2()
getPointX(point2) // OK
複製代碼
java
、c++
等傳統靜態類型語言是基於 名義子類型
的,必須顯示聲明子類型關係(繼承),才能夠兼容。
public class Main {
public static void main (String[] args) {
getPointX(new Point()); // ok
getPointX(new ChildPoint()); // ok
getPointX(new Point1()); // error
}
public static void getPointX (Point point) {
System.out.println(point.x);
}
static class Point {
public int x = 1;
}
static class Point2 {
public int x = 2;
}
static class ChildPoint extends Point {
public int x = 3;
}
}
複製代碼
子類型中必須包含源類型全部的屬性和方法:
function getPointX(point: { x: number }) {
return point.x
}
const point = {
x: 1,
y: '2'
}
getPointX(point) // OK
複製代碼
注意: 若是直接傳入一個對象字面量是會報錯的:
function getPointX(point: { x: number }) {
return point.x
}
getPointX({ x: 1, y: '2' }) // error
複製代碼
這是 ts 中的另外一個特性,叫作: excess property check
,當傳入的參數是一個對象字面量時,會進行額外屬性檢查。
介紹函數子類型前先介紹一下逆變與協變的概念,逆變與協變並非 TS 中獨有的概念,在其餘靜態語言中也有相關理念。
在介紹以前,先假設一個問題,約定以下標記:
A ≼ B
表示 A 是 B 的子類型,A 包含 B 的全部屬性和方法。A => B
表示以 A 爲參數,B 爲返回值的方法。 (param: A) => B
若是咱們如今有三個類型 Animal
、 Dog
、 WangCai(旺財)
,那麼確定存在下面的關係:
WangCai ≼ Dog ≼ Animal // 即旺財屬於狗屬於動物
複製代碼
問題:如下哪一種類型是 Dog => Dog
的子類呢?
WangCai => WangCai
WangCai => Animal
Animal => Animal
Animal => WangCai
從代碼來看解答
class Animal {
sleep: Function
}
class Dog extends Animal {
// 吠
bark: Function
}
class WangCai extends Dog {
dance: Function
}
function getDogName (cb: (dog: Dog) => Dog) {
const dog = cb(new Dog())
dog.bark()
}
// 對於入參來講,WangCai 是 Dog 的子類,Dog 類上沒有 dance 方法, 產生異常。
// 對於出參來講,WangCai 類繼承了 Dog 類,確定會有 bark 方法
getDogName((wangcai: WangCai) => {
wangcai.dance()
return new WangCai()
})
// 對於入參來講,WangCai 是 Dog 的子類,Dog 類上沒有 dance 方法, 產生異常。
// 對於出參來講,Animal 類上沒有 bark 方法, 產生異常。
getDogName((wangcai: WangCai) => {
wangcai.dance()
return new Animal()
})
// 對於入參來講,Animal 類是 Dog 的父類,Dog 類確定有 sleep 方法。
// 對於出參來講,WangCai 類繼承了 Dog 類,確定會有 bark 方法
getDogName((animal: Animal) => {
animal.sleep()
return new WangCai()
})
// 對於入參來講,Animal 類是 Dog 的父類,Dog 類確定有 sleep 方法。
// 對於出參來講,Animal 類上沒有 bark 方法, 產生異常。
getDogName((animal: Animal) => {
animal.sleep()
return new Animal()
})
複製代碼
能夠看到只有 Animal => WangCai
纔是 Dog => Dog
的子類型,能夠獲得一個結論,對於函數類型來講,函數參數的類型兼容是反向的,咱們稱之爲 逆變
,返回值的類型兼容是正向的,稱之爲 協變
。
逆變與協變的例子只說明瞭函數參數只有一個時的狀況,若是函數參數有多個時該如何區分?
其實函數的參數能夠轉化爲 Tuple
的類型兼容性:
type Tuple1 = [string, number]
type Tuple2 = [string, number, boolean]
let tuple1: Tuple1 = ['1', 1]
let tuple2: Tuple2 = ['1', 1, true]
let t1: Tuple1 = tuple2 // ok
let t2: Tuple2 = tuple1 // error
複製代碼
能夠看到 Tuple2 => Tuple1
,即長度大的是長度小的子類型,再因爲函數參數的逆變特性,因此函數參數少的能夠賦值給參數多的(參數從前日後需一一對應),從數組的 forEach 方法就能夠看出來:
[1, 2].forEach((item, index) => {
console.log(item)
}) // ok
[1, 2].forEach((item, index, arr, other) => {
console.log(other)
}) // error
複製代碼
聯合類型(union type)表示多種類型的 「或」 關係
function genLen(x: string | any[]) {
return x.length
}
genLen('') // ok
genLen([]) // ok
genLen(1) // error
複製代碼
交叉類型表示多種類型的 「與」 關係
interface Person {
name: string
age: number
}
interface Animal {
name: string
color: string
}
const x: Person & Animal = {
name: 'x',
age: 1,
color: 'red
}
複製代碼
使用聯合類型表示枚舉
type Position = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT'
const position: Position = 'UP'
複製代碼
能夠避免使用
enum
侵入了運行時。
ts 初學者很容易寫出下面的代碼:
function isString (value) {
return Object.prototype.toString.call(value) === '[object String]'
}
function fn (x: string | number) {
if (isString(x)) {
return x.length // error 類型「string | number」上不存在屬性「length」。
} else {
// .....
}
}
複製代碼
如何讓 ts 推斷出來上下文的類型呢?
1. 使用 ts 的 is
關鍵詞
function isString (value: unknown): value is string {
return Object.prototype.toString.call(value) === '[object String]'
}
function fn (x: string | number) {
if (isString(x)) {
return x.length
} else {
// .....
}
}
複製代碼
2. typeof 關鍵詞
在 ts 中,代碼實現中的 typeof 關鍵詞可以幫助 ts 判斷出變量的基本類型:
function fn (x: string | number) {
if (typeof x === 'string') { // x is string
return x.length
} else { // x is number
// .....
}
}
複製代碼
3. instanceof 關鍵詞
在 ts 中,instanceof 關鍵詞可以幫助 ts 判斷出構造函數的類型:
function fn1 (x: XMLHttpRequest | string) {
if (x instanceof XMLHttpRequest) { // x is XMLHttpRequest
return x.getAllResponseHeaders()
} else { // x is string
return x.length
}
}
複製代碼
4. 針對 null 和 undefined 的類型保護
在條件判斷中,ts 會自動對 null 和 undefined 進行類型保護:
function fn2 (x?: string) {
if (x) {
return x.length
}
}
複製代碼
5. 針對 null 和 undefined 的類型斷言
若是咱們已經知道的參數不爲空,可使用 !
來手動標記:
function fn2 (x?: string) {
return x!.length
}
複製代碼
typeof
關鍵詞除了作類型保護,還能夠從實現推出類型,。
注意:此時的
typeof
是一個類型關鍵詞,只能夠用在類型語法中。
function fn(x: string) {
return x.length
}
const obj = {
x: 1,
y: '2'
}
type T0 = typeof fn // (x: string) => number
type T1 = typeof obj // {x: number; y: string }
複製代碼
keyof
也是一個 類型關鍵詞 ,能夠用來取得一個對象接口的全部 key
值:
interface Person {
name: string
age: number
}
type PersonAttrs = keyof Person // 'name' | 'age'
複製代碼
in
也是一個 類型關鍵詞, 能夠對聯合類型進行遍歷,只能夠用在 type 關鍵詞下面。
type Person = {
[key in 'name' | 'age']: number
}
// { name: number; age: number; }
複製代碼
使用 []
操做符能夠進行索引訪問,也是一個 類型關鍵詞
interface Person {
name: string
age: number
}
type x = Person['name'] // x is string
複製代碼
寫一個類型複製的類型工具:
type Copy<T> = {
[key in keyof T]: T[key]
}
interface Person {
name: string
age: number
}
type Person1 = Copy<Person>
複製代碼
泛型至關於一個類型的參數,在 ts 中,泛型能夠用在 類
、接口
、方法
、類型別名
等實體中。
function createList<T>(): T[] {
return [] as T[]
}
const numberList = createList<number>() // number[]
const stringList = createList<string>() // string[]
複製代碼
有了泛型的支持,createList 方法能夠傳入一個類型,返回有類型的數組,而不是一個
any[]
。
若是咱們只但願 createList 函數只能生成指定的類型數組,該如何作,可使用 extends
關鍵詞來約束泛型的範圍和形狀。
type Lengthwise = {
length: number
}
function createList<T extends string | Lengthwise>(): T[] {
return [] as T[]
}
const numberList = createList<number>() // ok
const arrayList = createList<any[]>() // ok
const stringList = createList<boolean>() // error
複製代碼
由於
any[]
是一個數組類型,數組類型是有 length 參數的,因此 ok。
extends
除了作約束類型,還能夠作條件控制,至關於與一個三元運算符,只不過是針對 類型 的。
T extends U ? X : Y
若是 T 能夠被分配給 U,則返回 X,不然返回 Y。通常條件下,若是 T 是 U 的子類型,則認爲 T 能夠分配給 U。
type IsNumber<T> = T extends number ? true : false
type x = IsNumber<string> // false
複製代碼
映射類型至關與一個類型的函數,能夠作一些類型運算,輸入一個類型,輸出另外一個類型,前文咱們舉了個 Copy
的例子。
// 每個屬性都變成可選
type Partial<T> = {
[P in keyof T]?: T[P]
}
// 每個屬性都變成只讀
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
// 選擇對象中的某些屬性
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
// ......
複製代碼
typescript 2.8 在 lib.d.ts
中內置了幾個映射類型:
Partial<T>
-- 將 T
中的全部屬性變成可選。Readonly<T>
-- 將 T
中的全部屬性變成只讀。Pick<T, U>
-- 選擇 T
中能夠賦值給U
的類型。Exclude<T, U>
-- 從T
中剔除能夠賦值給U
的類型。Extract<T, U>
-- 提取T
中能夠賦值給U
的類型。NonNullable<T>
-- 從T
中剔除null
和undefined
。ReturnType<T>
-- 獲取函數返回值類型。InstanceType<T>
-- 獲取構造函數類型的實例類型。因此咱們平時寫 TS 時能夠直接使用這些類型工具:
interface ApiRes {
code: string;
flag: string;
message: string;
data: object;
success: boolean;
error: boolean;
}
type IApiRes = Pick<ApiRes, 'code' | 'flag' | 'message' | 'data'>
// {
// code: string;
// flag: string;
// message: string;
// data: object;
// }
複製代碼
對於 T extends U ? X : Y 來講,還存在一個特性,當 T 是一個聯合類型時,會進行條件分發。
type Union = string | number
type isNumber<T> = T extends number ? 'isNumber' : 'notNumber'
type UnionType = isNumber<Union> // 'notNumber' | 'isNumber'
複製代碼
實際上,extends 運算會變成以下形式:
(string extends number ? 'isNumber' : 'notNumber') | (number extends number ? 'isNumber' : 'notNumber')
複製代碼
Extract
就是基於此特性,再配合 never
幺元的特性實現的:
type Exclude<T, K> = T extends K ? never : T
type T1 = Exclude<string | number | boolean, string | boolean> // number
複製代碼
infer
關鍵詞infer
能夠對運算過程當中的類型進行存儲,內置的ReturnType
就是基於此特性實現的:
type ReturnType<T> =
T extends (...args: any) => infer R ? R : never
type Fn = (str: string) => number
type FnReturn = ReturnType<Fn> // number
複製代碼
默認狀況下,咱們所寫的代碼是位於全局模塊下的:
const foo = 2
複製代碼
此時,若是咱們建立了另外一個文件,並寫下以下代碼,ts 認爲是正常的:
const bar = foo // ok
複製代碼
若是要打破這種限制,只要文件中有 import
或者 export
表達式便可:
export const bar = foo // error
複製代碼
Tpescript 有兩種模塊的解析策略:Node 和 Classic。當 tsconfig.json 中 module
設置成 AMD、System、ES2015 時,默認爲 classic
,不然爲 Node
,也可使用 moduleResolution
手動指定模塊解析策略。
兩種模塊解析策略的區別在於,對於下面模塊引入來講:
import moduleB from 'moduleB'
複製代碼
Classic 模式的路徑尋址:
/root/src/folder/moduleB.ts
/root/src/folder/moduleB.d.ts
/root/src/moduleB.ts
/root/src/moduleB.d.ts
/root/moduleB.ts
/root/moduleB.d.ts
/moduleB.ts
/moduleB.d.ts
複製代碼
Node 模式的路徑尋址:
/root/src/node_modules/moduleB.ts
/root/src/node_modules/moduleB.tsx
/root/src/node_modules/moduleB.d.ts
/root/src/node_modules/moduleB/package.json (若是指定了"types"屬性)
/root/src/node_modules/moduleB/index.ts
/root/src/node_modules/moduleB/index.tsx
/root/src/node_modules/moduleB/index.d.ts
/root/node_modules/moduleB.ts
/root/node_modules/moduleB.tsx
/root/node_modules/moduleB.d.ts
/root/node_modules/moduleB/package.json (若是指定了"types"屬性)
/root/node_modules/moduleB/index.ts
/root/node_modules/moduleB/index.tsx
/root/node_modules/moduleB/index.d.ts
/node_modules/moduleB.ts
/node_modules/moduleB.tsx
/node_modules/moduleB.d.ts
/node_modules/moduleB/package.json (若是指定了"types"屬性)
/node_modules/moduleB/index.ts
/node_modules/moduleB/index.tsx
/node_modules/moduleB/index.d.ts
複製代碼
聲明文件已 .d.ts
結尾,用來描述代碼結構,通常用來爲 js 庫提供類型定義。
平時開發的時候有沒有這種經歷:當用npm安裝了某些包並使用的時候,會出現這個包的語法提示,下面是 vue
的提示:
這個語法提示就是聲明文件的功勞了,先來看一個簡單的聲明文件長啥樣,這是jsonp
這個庫的聲明文件:
type CancelFn = () => void;
type RequestCallback = (error: Error | null, data: any) => void;
interface Options {
param?: string;
prefix?: string;
name?: string;
timeout?: number;
}
declare function jsonp(url: string, options?: Options, cb?: RequestCallback): CancelFn;
declare function jsonp(url: string, callback?: RequestCallback): CancelFn;
export = jsonp;
複製代碼
有了這份聲明文件,編輯器在使用這個庫的時候就能夠根據這份聲明文件來作出相應的語法提示。
編輯器是怎麼找到這個聲明文件?
index.d.ts
,那麼這就是這個庫的聲明文件了。package.json
中有types
或者typings
字段,那個該字段指向的就是這個包的聲明文件。上述兩種都是將聲明文件寫在包裏面的狀況,若是某個庫很長時間不維護了,或者做者消失了該怎麼辦,不要緊,typescript
官方提供了一個聲明文件倉庫,嘗試使用@types
前綴來安裝某個庫的聲明文件:
npm i @types/lodash
複製代碼
當引入lodash
的時候,編輯器也會嘗試查找node_modules/@types/lodash
來爲你提供lodash
的語法提示。
還有一種就是本身寫聲明文件,編輯器會收集項目本地的聲明文件,若是某個包沒有聲明文件,你又想要語法提示,就能夠本身在本地寫個聲明文件:
// types/lodash.d.ts
declare module "lodash" {
export function chunk(array: any[], size?: number): any[];
export function get(source: any, path: string, defaultValue?: any): any;
}
複製代碼
若是源代碼是用ts
寫的,在編譯成js
的時候,只要加上-d
參數,就能生成對應的聲明文件。
tsc -d
複製代碼
聲明文件該怎麼寫能夠參考www.tslang.cn/docs/handbo…
還要注意的是,若是某個庫有聲明文件了,編輯器就不會再關心這個庫具體的代碼了,它只會根據聲明文件來作提示。
可能寫過 ts 的小夥伴有這樣的疑惑,我該如何在 window 對象上自定義屬性呢?
window.myprop = 1 // error
複製代碼
默認的,window 上是不存在 myprop
這個屬性的,因此不能夠直接賦值,固然,能夠輸用方括號賦值語句,可是 get 操做時也必須用 []
,而且沒有類型提示。
window['myprop'] = 1 // OK
window.myprop // 類型「Window & typeof globalThis」上不存在屬性「myprop」
window['myprop'] // ok,可是沒有提示,沒有類型
複製代碼
此時可使用聲明文件擴展其餘對象,在項目中隨便建一個xxx.d.ts
:
// index.d.ts
interface Window {
myprop: number
}
// index.ts
window.myprop = 2 // ok
複製代碼
也能夠在模塊內部擴展全局對象:
import A from 'moduleA'
window.myprop = 2
declare global {
interface Window {
myprop: number
}
}
複製代碼
若是使用過 ts 寫過 vue 的同窗,必定都碰到過這個問題,如何擴展 vue.prototype 上的屬性或者方法?
import Vue from 'vue'
Vue.prototype.myprops = 1
const vm = new Vue({
el: '#app'
})
// 類型「CombinedVueInstance<Vue, object, object, object, Record<never, any>>」
// 上不存在屬性「myprops」
console.log(vm.myprops)
複製代碼
vue 給出的方案,在項目中的 xxx.d.ts
中擴展 vue 實例上的屬性:
import Vue from 'vue'
declare module 'vue/types/vue' {
interface Vue {
myprop: number
}
}
複製代碼
ts 提供了 declare module 'xxx'
的語法來擴展其餘模塊,這很是有利於一些插件化的庫和包,例如 vue-router
擴展 vue
。
// vue-router/types/vue.d.ts
import Vue from 'vue'
import VueRouter, { Route, RawLocation, NavigationGuard } from './index'
declare module 'vue/types/vue' {
interface Vue {
$router: VueRouter
$route: Route
}
}
declare module 'vue/types/options' {
interface ComponentOptions<V extends Vue> {
router?: VueRouter
beforeRouteEnter?: NavigationGuard<V>
beforeRouteLeave?: NavigationGuard<V>
beforeRouteUpdate?: NavigationGuard<V>
}
}
複製代碼
.vue
文件引入?處理 vue
文件
對於全部以 .vue
結尾的文件,能夠默認導出 Vue 類型,這是符合 vue單文件組件
的規則的。
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
複製代碼
處理 css in js
對於全部的 .css
,能夠默認導出一個 any 類型的值,這樣能夠解決報錯問題,可是丟失了類型檢查。
declare module '*.css' {
const content: any
export default content
}
複製代碼
import * as React from 'react'
import * as styles from './index.css'
const Error = () => (
<div className={styles.centered}>
<div className={styles.emoji}>😭</div>
<p className={styles.title}>Ooooops!</p>
<p>This page doesn't exist anymore.</p>
</div>
)
export default Error
複製代碼
其實不論是全局擴展仍是模塊擴展,其實都是基於 TS 聲明合併 的特性,簡單來講,TS 會將它收集到的一些同名的接口、類、類型別名按照必定的規則進行合併。
ts 內置了一個 compiler
(tsc),可讓咱們把 ts 文件編譯成 js 文件,配合衆多的編譯選項,有時候不須要 babel
咱們就能夠完成大多數工做。
tsc 在編譯 ts 代碼的時候,會根據 tsconfig.json
配置文件的選項採起不一樣的編譯策略。下面是三個經常使用的配置項:
和 babel
同樣,ts 在編譯的時候只會轉化新 語法,不會轉化新的 API, 因此有些場景下須要自行處理 polyfill
的問題。
tsconfig
中的 outDir
字段能夠配置編譯後的文件目錄,有利於 dist 的統一管理。
{
"compilerOptions": {
"module": "umd",
"outDir": "./dist"
}
}
複製代碼
編譯後的目錄結構:
myproject
├── dist
│ ├── index.js
│ └── lib
│ └── moduleA.js
├── index.ts
├── lib
│ └── moduleA.ts
└── tsconfig.json
複製代碼
對於 amd
和 system
模塊,能夠配置 tsconfig.json
中的 outFile
字段,輸出爲一個 js 文件。
若是須要輸出成其餘模塊,例如 umd
,又但願打包成一個單獨的文件,須要怎麼作?
可使用 rollup
或者 webpack
:
// rollup.config.js
const typescript = require('rollup-plugin-typescript2')
module.exports = {
input: './index.ts',
output: {
name: 'MyBundle',
file: './dist/bundle.js',
format: 'umd'
},
plugins: [
typescript()
]
}
複製代碼
@types
倉庫你們在平常開發的時候,可能會常常用到webpack的路徑別名,好比: import xxx from '@/path/to/name'
,若是編輯器不作任何配置的話,這樣寫會很尷尬,編譯器不會給你任何路徑提示,更不會給你語法提示。這裏有個小技巧,基於 tsconfig.json
的 baseUrl
和paths
這兩個字段,配置好這兩個字段後,.ts
文件裏不但有了路徑提示,還會跟蹤到該路徑進行語法提示。
這裏有個小彩蛋,能夠把 tsconfig.json
重命名成jsconfig.json
,.js
文件裏也能享受到路徑別名提示和語法提示了。
使用 webstorm
的同窗若是也想使用的話,只要打開設置,搜索webpack,而後設置一下webpack配置文件的路徑就行了。