Stack Overflow 在 90,000 名開發者中開展了一項調查,結果顯示 TypeScript 是人們最想學習的工具之一。html
在過去幾年中,TypeScript 的熱門程度、社區規模和使用率都在不斷提升。現在,甚至 Facebook 正將 Jest 項目轉移至 TypeScript。react
TypeScript 是 JavaScript 的超集,具備靜態類型特性,旨在簡化大型 JavaScript 應用程序的開發,也被稱爲 JavaScript that scales(可拓展的 JavaScript)。git
JavaScript 在過去幾年中快速發展,成爲客戶端和服務器端最通用的跨平臺語言。github
但 JavaScript 本意並不用於大型應用開發。它是一種沒有類型系統的動態語言,也就是說,變量的值能夠是任何類型(例如字符串或布爾值)。typescript
而類型系統可以提升代碼質量和可讀性,使代碼庫更易於維護或重構。更重要的是它能夠在編譯時就捕獲錯誤,而不是在運行時才捕獲。數組
而 JavaScript 並無類型系統,因此一個大型開發團隊難以使用 JavaScript 構建複雜的應用程序。安全
而 TypeScript 能在編譯時檢查不一樣部分代碼的正確性。在編譯時檢查出錯誤,便於開發者發現錯誤的位置和具體問題。若是運行時才檢查出錯誤,則須要跟蹤複雜的堆棧,花費大量時間進行調試。bash
const isLoading: boolean = false;
複製代碼
const decimal: number = 8;
const binary: number = 0b110;
複製代碼
const fruit: string = "orange";
複製代碼
數組能夠寫成下面兩種形式:服務器
// 最多見的方式
let firstFivePrimes: number[] = [2, 3, 5, 7, 11];
// 不太常見的方式:使用泛型 (稍後介紹)
let firstFivePrimes2: Array<number> = [2, 3, 5, 7, 11];
複製代碼
Tuple 類型表示一種組織好的數組,元素的類型預先知道,而且數量固定。這意味着你有可能獲得錯誤提示:dom
let contact: [string, number] = ['John', 954683];
contact = ['Ana', 842903, 'extra argument'] /* Error!
Type '[string, number, string]' is not assignable to type '[string, number]'. */
複製代碼
any
與類型系統中的任何類型都兼容。意味着能夠將任何內容賦值給它,也能夠將它賦值給任何類型。它能讓你避開類型檢查。
let variable: any = 'a string';
variable = 5;
variable = false;
variable.someRandomMethod(); /* 行吧,
也許運行的時候 someRandomMethod 是存在的 */
複製代碼
void
表示沒有任何類型。它一般用做沒有返回值的函數的返回類型。
function sayMyName(name: string): void {
console.log(name);
}
sayMyName('Heisenberg');
複製代碼
never
類型表示的是那些永不存在的值的類型。 例如,never
類型是那些老是會拋出異常、或者根本就不會有返回值的函數的返回值類型。
// 拋出異常
function error(message: string): never {
throw new Error(message);
}
複製代碼
undefined
和 null
二者各自有本身的類型分別叫作 undefined
和 null
。和 void
類似,它們的自己的類型用處不是很大,可是在聯合類型中很是有用 (稍後介紹)。
type someProp = string | null | undefined;
複製代碼
TypeScript 3.0 引入了 unknown (未知) 類型,它是與 any
類型對應的安全類型。任何東西均可以賦值給 unknown
,但 unknown
不能賦值給除了它自己和 any
之外的任何東西。在沒有先斷言或指定到更具體類型的狀況下,不容許對 unknown
進行任何操做。
type I1 = unknown & null; // null
type I2 = unknown & string; // string
type U1 = unknown | null; // unknown
type U2 = unknown | string; // unknown
複製代碼
類型別名能夠爲現有類型提供替代名稱,以便某些地方使用。構造它的語法以下:
type Login = string;
複製代碼
TypeScript 容許讓一個屬性具備多種數據類型,名爲 union (聯合) 類型。
type Password = string | number;
複製代碼
交叉類型是將多種類型疊加到一塊兒成爲一種類型。
interface Person {
name: string;
age: number;
}
interface Worker {
companyId: string;
}
type Employee = Person & Worker;
複製代碼
接口好似你和編譯器定義契約,由你指定一個類型,預期它的屬性應該是些什麼類型。
邊注:接口不受 JavaScript 運行時的特性影響,它只在類型檢查中會用到。
?
標記),意味着接口的對象可能會、也可能不會定義這些屬性。interface ICircle {
readonly id: string;
center: {
x: number;
y: number;
},
radius: number;
color?: string; // 可選屬性
}
const circle1: ICircle = {
id: '001',
center: { x: 0 },
radius: 8,
}; /* Error! Property 'y' is missing in type '{ x: number; }'
but required in type '{ x: number; y: number; }'. */
複製代碼
接口能夠擴展成另外一個接口,或者更多接口。這使得接口的編寫更具備靈活性和複用性。
interface ICircleWithArea extends ICircle {
getArea: () => number;
}
複製代碼
實現接口的類須要嚴格遵循接口的結構。
interface IClock {
currentTime: Date;
setTime(d: Date): void;
}
複製代碼
enum
(枚舉) 用來組織一組的相關值,這些值能夠是數值,也能夠是字符串值。
enum CardSuit {
Clubs,
Diamonds,
Hearts,
Spades
}
let card = CardSuit.Clubs;
複製代碼
默認狀況下,枚舉的本質是數字。enum
的取值從 0 開始,以 1 遞增。
上一個例子所生成的 JavaScript 代碼以下:
var CardSuit;
(function (CardSuit) {
CardSuit[CardSuit["Clubs"] = 0] = "Clubs";
CardSuit[CardSuit["Diamonds"] = 1] = "Diamonds";
CardSuit[CardSuit["Hearts"] = 2] = "Hearts";
CardSuit[CardSuit["Spades"] = 3] = "Spades";
})(CardSuit || (CardSuit = {}));
複製代碼
或者,枚舉能夠用字符串值來初始化,這種方法更易讀。
enum SocialMedia {
Facebook = 'FACEBOOK',
Twitter = 'TWITTER',
Instagram = 'INSTAGRAM',
LinkedIn = 'LINKEDIN'
}
複製代碼
enum
支持反向映射,也就是說,能夠經過值來得到成員、成員名。
回顧以前 CardSuit 的例子:
const clubsAsNumber: number = CardSuit.Clubs; // 3
const clubsAsString: string = CardSuit[0]; // 'Clubs'
複製代碼
你能夠爲每一個參數指定一個類型,再爲函數指定一個返回類型。
function add(x: number, y: number): number {
return x + y;
}
複製代碼
TypeScript 容許聲明 函數重載。簡單來講,可使用多個名稱相同但參數類型和返回類型不一樣的函數。參考下面的例子:
function padding(a: number, b?: number, c?: number, d?: any) {
if (b === undefined && c === undefined && d === undefined) {
b = c = d = a;
}
else if (c === undefined && d === undefined) {
c = a;
d = b;
}
return {
top: a,
right: b,
bottom: c,
left: d
};
}
複製代碼
參數的含義根據傳遞給函數的參數數量而變化。此外,該函數只接受一個、兩個或四個參數。要構造函數重載,只需屢次聲明函數頭就能夠了。最後一個函數頭真正實現了函數體,但函數外部並不能直接調用最後一個函數頭。
function padding(all: number);
function padding(topAndBottom: number, leftAndRight: number);
function padding(top: number, right: number, bottom: number, left: number);
function padding(a: number, b?: number, c?: number, d?: number) {
if (b === undefined && c === undefined && d === undefined) {
b = c = d = a;
}
else if (c === undefined && d === undefined) {
c = a;
d = b;
}
return {
top: a,
right: b,
bottom: c,
left: d
};
}
複製代碼
你能夠指定屬性的類型和方法參數的類型。
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet(name: string) {
return Hi <span class="hljs-subst" style="color: rgb(51, 51, 51); font-weight: normal;">${name}</span>, <span class="hljs-subst" style="color: rgb(51, 51, 51); font-weight: normal;">${<span class="hljs-keyword" style="color: rgb(51, 51, 51); font-weight: bold;">this</span>.greeting}</span>;
}
}
複製代碼
Typescript 支持 public
(公有), private
(私有), protected
(保護) 修飾符,它們決定了類成員的可訪問性。
public
(公有) 成員和純 JavaScript 的成員同樣,是默認的修飾符。private
(私有) 成員對外界來講不可訪問。protected
(保護) 成員和私有成員的區別在於,它可以被繼承類訪問。| 具備訪問權限 | public | protected | private |
| :------------- | :----: | :-------: | :-----: |
| 類自己 | yes | yes | yes |
| 派生類 | yes | yes | no |
| 類實例 | yes | no | no |
複製代碼
readonly
(只讀) 變量必須在它聲明或構造時初始化。
class Spider {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
this.name = theName;
}
}
複製代碼
參數屬性 能夠放在一個地方建立並初始化成員。它經過給構造函數參數添加一個訪問限定符來聲明。
class Spider {
readonly numberOfLegs: number = 8;
constructor(readonly name: string) {
}
}
複製代碼
abstract (抽象) 這個關鍵字能夠用在抽象類上,也能夠用在抽象類方法上。
TypeScript 容許你以任何方式覆蓋其推斷的類型。當你比編譯器自己能更好地理解變量類型時,可使用它。
const friend = {};
friend.name = 'John'; // Error! Property 'name' does not exist on type '{}'
interface Person {
name: string;
age: number;
}
複製代碼
最初,類型斷言的語法是 <type>
let person = <Person> {};
複製代碼
但這在 JSX 中使用時產生了歧義。所以建議使用 as
代替。
類型斷言一般在從 JavaScript 遷移代碼時使用,你對變量的類型瞭解可能比當前指派的更準確。
但斷言也會 被認爲有害。
咱們來看看上一個示例中的 Person 接口,你注意到了什麼問題嗎?若是你注意到丟失了 age 屬性,恭喜,你對了!編譯器可能會幫助你自動完成 Person 的屬性,但若是您遺漏了任何屬性,它也不會報錯。
沒有明確指定出類型時,TypeScript 會推斷變量類型。
/**
變量聲明
/
let a = "some string";
let b = 1;
a = b; // Error! Type 'number' is not assignable to type 'string'.
複製代碼
類型兼容性是基於結構類型的,結構類型只使用其成員來描述類型。
結構化類型系統的基本規則是:若是 x
要兼容 y
,那麼 y
至少具備與 x
相同的屬性。
interface Person {
name: string;
}
複製代碼
因爲 y
有一個成員 name: string
匹配 Person 接口所需的屬性,這意味着 x
是 y
的子類型。所以這個賦值是合法的。
參數數量
在函數調用中,至少須要傳入足夠的參數,多餘的參數不會致使任何錯誤。
function consoleName(person: Person) {
console.log(person.name);
}
consoleName({ name: 'John' }); // 正確
consoleName({ name: 'John', age: 20 }); // 多餘的參數也合法
複製代碼
返回值類型
返回值類型必須至少包含足夠的數據。
let x = () => ({name: 'John'});
let y = () => ({name: 'John', age: 20 });
x = y; // 正確
y = x; /* Error! Property 'age' is missing in type '{ name: string; }'
but required in type '{ name: string; age: number; }' */
複製代碼
類型保護能夠在條件塊中縮小對象類型的範圍。
在條件裏使用 typeof,編譯器會知道變量的類型會不一致。在下面的示例中,TypeScript 會知道:在條件塊以外,x
多是布爾值,而布爾值上沒法調用函數 toFixed
。
function example(x: number | boolean) {
if (typeof x === 'number') {
return x.toFixed(2);
}
return x.toFixed(2); // Error! Property 'toFixed' does not exist on type 'boolean'.
}
複製代碼
class MyResponse {
header = 'header example';
result = 'result example';
// ...
}
class MyError {
header = 'header example';
message = 'message example';
// ...
}
function example(x: MyResponse | MyError) {
if (x instanceof MyResponse) {
console.log(x.message); // Error! Property 'message' does not exist on type 'MyResponse'.
console.log(x.result); // 正確
} else {
// TypeScript 知道這裏必定是 MyError
console.log(x.message); // 正確
console.log(x.result); // Error! Property 'result' does not exist on type 'MyError'.
}
}
複製代碼
in
運算符會檢查一個屬性在某對象上是否存在。
interface Person {
name: string;
age: number;
}
const person: Person = {
name: 'John',
age: 28,
};
複製代碼
字面量正是 JavaScript 原始數據類型具體的值,它們能夠與 union (聯合) 類型搭配使用,構造一些實用的概念。
type Orientation = 'landscape' | 'portrait';
function changeOrientation(x: Orientation) {
// ...
}
changeOrientation('portrait'); // 正確
changeOrientation('vertical'); /* Error! Argument of type '"vertical"' is not
assignable to parameter of type 'Orientation'. /
複製代碼
條件類型表示類型關係的測試,並根據測試的結果選擇兩種可能類型中的一種。
type X = A extends B ? C : D;
複製代碼
若是 A
類型能夠賦值給 B
類型,那麼 X
是 C
類型;不然 X
是 D
類型。
泛型是必須包含或引用其餘類型才能完成的類型。它增強了變量之間有意義的約束。
下面例子中的函數會返回所傳入的任何類型的數組。
function reverse<T>(items: T[]): T[] {
return items.reverse();
}
reverse([1, 2, 3]); // number[]
reverse([0, true]); // (number | boolean)[]
複製代碼
keyof
運算符會查詢給定類型的鍵集。
interface Person {
name: string;
age: number;
}
type PersonKeys = keyof Person; // 'name' | 'age'
複製代碼
映射類型,經過在屬性類型上創建映射,從現有的類型建立新類型。具備已知類型的每一個屬性都會根據你指定的規則進行轉換。
type Partial<T> = {
[P in keyof T]?: T[P];
}
複製代碼
T
。keyof T
表示全部 T
類型屬性的名字(字符串字面類型)的聯合。[P in keyof T]?: T[P]
表示全部 T
類型的屬性 P
的類型都應該是可選的,而且都會被轉換爲 T[P]
。T[P]
表示 T
類型的屬性 P
的類型。正如在接口部分中所介紹的,TypeScript 中能夠建立只讀屬性。 Readonly
類型接受一個類型 T
,並將其全部屬性設置爲只讀。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
複製代碼
Exclude
能夠從其餘類型中排除某些類型。排除的是能夠賦值給 T
的屬性。
/**
type Exclude<T, U> = T extends U ? never : T;
/
type User = {
_id: number;
name: string;
email: string;
created: number;
};
複製代碼
Pick
能夠從其餘類型中選取某些類型。 挑選的是能夠賦值給 T
的屬性。
/**
複製代碼
你可使用 infer
關鍵字來推斷條件類型的 extends
子句中的類型變量。這樣的推斷類型變量只能用於條件類型的 true 分支。
獲取函數的返回類型。
/**
原版的 TypeScript's ReturnType type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; / type MyReturnType<T> = T extends (...args: any) => infer R ? R : any; 複製代碼
咱們來拆解 MyReturnType
:
T
的返回類型是 …T
是否是一個函數?R
;any
。basarat.gitbooks.io/typescript/
www.typescriptlang.org/docs/home.h…
www.tutorialsteacher.com/typescript
爲了達到學習和實踐 TypeScript 的目的,我用 TS 和 React-Native(用了 hooks)構建了一個簡單的 CurrencyConverter (匯率轉換) 程序。你能夠在 這裏 查看這個項目。
感謝、祝賀你閱讀到這裏!若是你對此有任何想法,請隨時發表評論。