快速入門:讓你可以看懂TypeScript的簡單代碼
一:類型注解
TypeScript 是 JavaScript 的「強(qiáng)類型版本」。
它可以提供了靜態(tài)的代碼分析往枷,分析代碼結(jié)構(gòu)和提供的類型注解。要注意的是盡管有錯誤展东,xxx.js文件還是被創(chuàng)建了哥蔚。 就算你的代碼里有錯誤,你仍然可以使用TypeScript写隶。但在這種情況下,TypeScript會警告你代碼可能不會按預(yù)期執(zhí)行讲仰。
TypeScript 雖然是強(qiáng)類型語言慕趴,但是如果對象被聲明為了 any 類型,就會忽略所有的類型檢查鄙陡。這種靈活的結(jié)構(gòu)保證了他可以在保證整體有強(qiáng)類型檢查優(yōu)勢的同時冕房,在一些細(xì)節(jié)問題上保持弱類型的靈活。
必須指定數(shù)據(jù)類型(給人理解將數(shù)據(jù)類型分成3種)
1.js有的類型
boolean類型趁矾、number類型耙册、string類型、array類型毫捣、undefined详拙、null
2.ts多出的類型
tuple類型(元組類型)、enum類型(枚舉類型)蔓同、any類型(任意類型)
3.特別的類型
void類型(沒有任何類型)表示定義方法沒有返回值
never類型:是其他類型(包括null和undefined)的子類型饶辙,代表從不會出現(xiàn)的值
這意味著聲明never變量只能被never類型所賦值
二:接口
interface Person {
firstName: string;
lastName: string;
}
function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = { firstName: "Jane", lastName: "User" };
document.body.innerHTML = greeter(user);
這里我們使用接口來描述一個擁有firstName和lastName字段的對象。 在TypeScript里斑粱,只在兩個類型內(nèi)部的結(jié)構(gòu)兼容那么這兩個類型就是兼容的弃揽。 這就允許我們在實(shí)現(xiàn)接口時候只要保證包含了接口要求的結(jié)構(gòu)就可以,而不必明確地使用 implements語句。
三:類
interface Person {
firstName: string;
lastName: string;
}
class Student <Person> {
fullName: string;
constructor(public firstName, public middleInitial, public lastName) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
}
function greeter(person : Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = new Student("Jane", "M.", "User");
document.body.innerHTML = greeter(user);
TypeScript支持JavaScript的新特性->基于類的面向?qū)ο缶幊?/h6>
Student類矿微,它帶有一個構(gòu)造函數(shù)和一些公共字段痕慢。
注意類和接口可以一起共作,程序員可以自行決定抽象的級別冷冗。
在構(gòu)造函數(shù)的參數(shù)上使用public等同于創(chuàng)建了同名的成員變量守屉。
詳細(xì)指南
一:基礎(chǔ)類型(官方文檔復(fù)制粘貼)
1,布爾值
let isDone: boolean = false;
2蒿辙,數(shù)字
JavaScript一樣拇泛,TypeScript里的所有數(shù)字都是浮點(diǎn)數(shù)。 這些浮點(diǎn)數(shù)的類型是 number思灌。
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;
3俺叭,字符串
使用 string表示文本數(shù)據(jù)類型。 和JavaScript一樣泰偿,可以使用雙引號( ")或單引號(')表示字符串熄守。
let name: string = "bob";
name = "smith";
你還可以使用模版字符串,它可以定義多行文本和內(nèi)嵌表達(dá)式耗跛。 這種字符串是被反引號包圍( `)裕照,并且以${ expr }這種形式嵌入表達(dá)式
let name: string = `Gene`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ name }.
I'll be ${ age + 1 } years old next month.`;
這與下面定義sentence的方式效果相同:
let sentence: string = "Hello, my name is " + name + ".\n\n" +
"I'll be " + (age + 1) + " years old next month.";
4,數(shù)組
有兩種方式可以定義數(shù)組调塌。
1晋南,let list: number[] = [1, 2, 3];
2,let list: Array<number> = [1, 2, 3];
5羔砾,元組 Tuple
元組類型允許表示一個已知元素數(shù)量和類型的數(shù)組负间,各元素的類型不必相同
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
6,枚舉
enum類型是對JavaScript標(biāo)準(zhǔn)數(shù)據(jù)類型的一個補(bǔ)充姜凄。 「使用枚舉類型可以為一組數(shù)值賦予友好的名字」政溃。
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;
7铃剔,Any
不希望類型檢查器對這些值進(jìn)行檢查而是直接讓它們通過編譯階段的檢查誉结。 那么我們可以使用 any類型來標(biāo)記這些變量
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
8,Void
它表示沒有任何類型获讳。 當(dāng)一個函數(shù)沒有返回值時申鱼,你通常會見到其返回值類型是 void
function warnUser(): void {
console.log("This is my warning message");
}
聲明一個void類型的變量沒有什么大用空扎,因為你只能為它賦予undefined和null:
let unusable: void = undefined;
9,Null 和 Undefined
TypeScript里润讥,undefined和null兩者各自有自己的類型分別叫做undefined和null
// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;
默認(rèn)情況下null和undefined是所有類型的子類型。 就是說你可以把 null和undefined賦值給number類型的變量盘寡。
10楚殿,Never
never類型表示的是那些永不存在的值的類型。這貨有啥用?
11脆粥,Object
object表示非原始類型砌溺,也就是除number,string变隔,boolean规伐,symbol,null或undefined之外的類型匣缘。
使用object類型猖闪,就可以更好的表示像Object.create這樣的API
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
12, 類型斷言
對方不想和你說話肌厨,并向你拋出一段代碼
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
二:變量聲明
let與var的對話
var:爾等何德何能培慌,竟敢替代我
let:當(dāng)我聲明一個變量時,使用的是詞法作用域或塊作用域
var:再見
const:阿彌陀佛
const numLivesForCat = 9;
被賦值后不能再改變
解構(gòu)
1柑爸,解構(gòu)數(shù)組
let input = [1, 2];
let [first, second] = input;
console.log(first); // outputs 1
console.log(second); // outputs 2
這創(chuàng)建了2個命名變量 first 和 second吵护。 相當(dāng)于使用了索引,但更為方便
first = input[0];
second = input[1];
解構(gòu)作用于已聲明的變量會更好:
// swap variables
[first, second] = [second, first];
作用于函數(shù)參數(shù):
function f([first, second]: [number, number]) {
console.log(first);
console.log(second);
}
f(input);
你可以在數(shù)組里使用...語法創(chuàng)建剩余變量:
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // outputs 1
console.log(rest); // outputs [ 2, 3, 4 ]
當(dāng)然表鳍,由于是JavaScript, 你可以忽略你不關(guān)心的尾隨元素:
復(fù)制到剪切板
let [first] = [1, 2, 3, 4];
console.log(first); // outputs 1
或其它元素:
復(fù)制到剪切板
let [, second, , fourth] = [1, 2, 3, 4];
2, 其他(我不想寫了馅而,暫時不知道這貨有啥用)
三:接口
TypeScript的核心原則之一是對值所具有的結(jié)構(gòu)進(jìn)行類型檢查。 它有時被稱做“鴨式辨型法”或“結(jié)構(gòu)性子類型化”譬圣。 在TypeScript里瓮恭,接口的作用就是為這些類型命名和為你的代碼或第三方代碼定義契約。
通過接口胁镐,更好的理解【對值所具有的結(jié)構(gòu)進(jìn)行類型檢查】
1偎血,接口初探
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
類型檢查器會查看printLabel的調(diào)用。 printLabel有一個參數(shù)盯漂,并要求這個對象參數(shù)有一個名為label類型為string的屬性颇玷。 需要注意的是,我們傳入的對象參數(shù)實(shí)際上會包含很多屬性就缆,但是編譯器只會檢查那些必需的屬性是否存在帖渠,并且其類型是否匹配
這次使用接口來描述:必須包含一個label屬性且類型為string:
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
LabelledValue接口就好比一個名字,用來描述上面例子里的要求竭宰。 它代表了有一個 label屬性且類型為string的對象空郊。 需要注意的是,我們在這里并不能像在其它語言里一樣切揭,說傳給 printLabel的對象實(shí)現(xiàn)了這個接口狞甚。我們只會去關(guān)注值的外形。 只要傳入的對象滿足上面提到的必要條件廓旬,那么它就是被允許的哼审。
還有一點(diǎn)值得提的是,類型檢查器不會去檢查屬性的順序,只要相應(yīng)的屬性存在并且類型也是對的就可以涩盾。
2十气,可選屬性
接口里的屬性不全都是必需的。 有些是只在某些條件下存在春霍,或者根本不存在砸西。 可選屬性在應(yīng)用“option bags”模式時很常用,即給函數(shù)傳入的參數(shù)對象中只有部分屬性賦值了址儒。
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): {color: string; area: number} {
let newSquare = {color: "white", area: 100};
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({color: "black"});
帶有可選屬性的接口與普通的接口定義差不多芹枷,只是在可選屬性名字定義的后面加一個?符號
可選屬性的好處之一是可以對可能存在的屬性進(jìn)行預(yù)定義,好處之二是可以捕獲引用了不存在的屬性時的錯誤离福。
3杖狼,只讀屬性
一些對象屬性只能在對象剛剛創(chuàng)建的時候修改其值。 你可以在屬性名前用 readonly來指定只讀屬性:
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!
番外篇
TypeScript具有ReadonlyArray<T>類型妖爷,它與Array<T>相似蝶涩,只是把所有可變方法去掉了,因此可以確保數(shù)組創(chuàng)建后再也不能被修改:
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
let isDone: boolean = false;
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;
let name: string = "bob";
name = "smith";
let name: string = `Gene`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ name }.
I'll be ${ age + 1 } years old next month.`;
let sentence: string = "Hello, my name is " + name + ".\n\n" +
"I'll be " + (age + 1) + " years old next month.";
1晋南,let list: number[] = [1, 2, 3];
2,let list: Array<number> = [1, 2, 3];
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
function warnUser(): void {
console.log("This is my warning message");
}
let unusable: void = undefined;
// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
const numLivesForCat = 9;
let input = [1, 2];
let [first, second] = input;
console.log(first); // outputs 1
console.log(second); // outputs 2
first = input[0];
second = input[1];
// swap variables
[first, second] = [second, first];
function f([first, second]: [number, number]) {
console.log(first);
console.log(second);
}
f(input);
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // outputs 1
console.log(rest); // outputs [ 2, 3, 4 ]
復(fù)制到剪切板
let [first] = [1, 2, 3, 4];
console.log(first); // outputs 1
復(fù)制到剪切板
let [, second, , fourth] = [1, 2, 3, 4];
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): {color: string; area: number} {
let newSquare = {color: "white", area: 100};
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({color: "black"});
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
####### 上面代碼的最后一行絮识,可以看到就算把整個ReadonlyArray賦值到一個普通數(shù)組也是不可以的绿聘。 但是你可以用類型斷言重寫:
a = ro as number[]
插播一條廣告 :readonly vs const
最簡單判斷該用readonly還是const的方法是看要把它做為變量使用還是做為一個屬性。 做為變量使用的話用 const次舌,若做為屬性則使用readonly熄攘。
4,函數(shù)類型
這里強(qiáng)調(diào)一下彼念。我們在看接口相關(guān)的東西挪圾。不要插播完廣告,你就把前面劇情忘了逐沙。
接口能夠描述JavaScript中對象擁有的各種各樣的外形哲思。 除了描述帶有屬性的普通對象外,接口也可以描述函數(shù)類型吩案。
為了使用接口表示函數(shù)類型棚赔,我們需要給接口定義一個調(diào)用簽名。 它就像是一個只有參數(shù)列表和返回值類型的函數(shù)定義徘郭。參數(shù)列表里的每個參數(shù)都需要名字和類型靠益。
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
let result = source.search(subString);
return result > -1;
}
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
let result = src.search(sub);
return result > -1;
}
函數(shù)的參數(shù)會逐個進(jìn)行檢查,要求對應(yīng)位置上的參數(shù)類型是兼容的残揉。 如果你不想指定類型胧后,TypeScript的類型系統(tǒng)會推斷出參數(shù)類型,因為函數(shù)直接賦值給了 SearchFunc類型變量抱环。 函數(shù)的返回值類型是通過其返回值推斷出來的(此例是 false和true)绩卤。 如果讓這個函數(shù)返回數(shù)字或字符串途样,類型檢查器會警告我們函數(shù)的返回值類型與 SearchFunc接口中的定義不匹配。
5,可索引的類型
自行看官方文檔吧濒憋。目前太燒腦。
6陶夜,類類型
自行看官方文檔吧凛驮。目前太燒腦。
7条辟,繼承接口
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
===
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
8黔夭,混合類型
自行看官方文檔吧。目前太燒腦羽嫡。
9本姥,接口繼承類
自行看官方文檔吧。目前太燒腦杭棵。
你不能指望看了這么一篇抄來的文章就啥都懂了婚惫。
這,只是個新手整理出來的入門筆記魂爪,而已O认稀!
四:類
這里假設(shè)你熟知面向?qū)ο缶幊獭?/h6>
1滓侍,類
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
引用任何一個類成員的時候都用了 this蒋川。 它表示我們訪問的是類的成員。
使用 new構(gòu)造了 Greeter類的一個實(shí)例撩笆。 它會調(diào)用之前定義的構(gòu)造函數(shù)捺球,創(chuàng)建一個 Greeter類型的新對象,并執(zhí)行構(gòu)造函數(shù)初始化它夕冲。
2氮兵,繼承
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 45) {
console.log("Galloping...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);
派生類包含了一個構(gòu)造函數(shù),它 必須調(diào)用 super()耘擂,它會執(zhí)行基類的構(gòu)造函數(shù)胆剧。 而且,在構(gòu)造函數(shù)里訪問 this的屬性之前醉冤,我們 一定要調(diào)用 super()秩霍。 這個是TypeScript強(qiáng)制執(zhí)行的一條重要規(guī)則。
3蚁阳,公共铃绒,私有與受保護(hù)的修飾符
默認(rèn)為 public
理解 private
TypeScript使用的是結(jié)構(gòu)性類型系統(tǒng)。 當(dāng)我們比較兩種不同的類型時螺捐,并不在乎它們從何處而來颠悬,如果所有成員的類型都是兼容的矮燎,我們就認(rèn)為它們的類型是兼容的。
如果其中一個類型里包含一個 private成員赔癌,那么只有當(dāng)另外一個類型中也存在這樣一個 private成員诞外, 并且它們都是來自同一處聲明時,我們才認(rèn)為這兩個類型是兼容的灾票。 對于 protected成員也使用這個規(guī)則峡谊。
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
class Rhino extends Animal {
constructor() { super("Rhino"); }
}
class Employee {
private name: string;
constructor(theName: string) { this.name = theName; }
}
let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");
animal = rhino;
animal = employee; // 錯誤: Animal 與 Employee 不兼容.
理解 protected
protected修飾符與 private修飾符的行為很相似,但有一點(diǎn)不同刊苍, protected成員在派生類中仍然可以訪問
class Person {
protected name: string;
constructor(name: string) { this.name = name; }
}
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name)
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // 錯誤
不能在 Person類外使用 name既们,但是我們?nèi)匀豢梢酝ㄟ^ Employee類的實(shí)例方法訪問,因為 Employee是由 Person派生而來的
class Person {
protected name: string;
protected constructor(theName: string) { this.name = theName; }
}
// Employee 能夠繼承 Person
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
let john = new Person("John"); // 錯誤: 'Person' 的構(gòu)造函數(shù)是被保護(hù)的.
構(gòu)造函數(shù)也可以被標(biāo)記成 protected正什。 這意味著這個類不能在包含它的類外被實(shí)例化啥纸,但是能被繼承
readonly修飾符
你可以使用 readonly關(guān)鍵字將屬性設(shè)置為只讀的。 只讀屬性必須在聲明時或構(gòu)造函數(shù)里被初始化
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
this.name = theName;
}
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // 錯誤! name 是只讀的.
參數(shù)屬性
class Octopus {
readonly numberOfLegs: number = 8;
constructor(readonly name: string) {
}
}
注意看我們是如何舍棄了 theName婴氮,僅在構(gòu)造函數(shù)里使用 readonly name: string參數(shù)來創(chuàng)建和初始化 name成員斯棒。 我們把聲明和賦值合并至一處。
參數(shù)屬性通過給構(gòu)造函數(shù)參數(shù)前面添加一個訪問限定符來聲明莹妒。 使用 private限定一個參數(shù)屬性會聲明并初始化一個私有成員名船;對于 public和 protected來說也是一樣。
存取器
TypeScript支持通過getters/setters來截取對對象成員的訪問旨怠。 它能幫助你有效的控制對對象成員的訪問渠驼。
let passcode = "secret passcode";
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (passcode && passcode == "secret passcode") {
this._fullName = newName;
}
else {
console.log("Error: Unauthorized update of employee!");
}
}
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
alert(employee.fullName);
}
首先,存取器要求你將編譯器設(shè)置為輸出ECMAScript 5或更高鉴腻。 不支持降級到ECMAScript 3迷扇。 其次,只帶有 get不帶有 set的存取器自動被推斷為 readonly爽哎。 這在從代碼生成 .d.ts文件時是有幫助的蜓席,因為利用這個屬性的用戶會看到不允許夠改變它的值。
靜態(tài)屬性
可以創(chuàng)建類的靜態(tài)成員课锌,這些屬性存在于類本身上面而不是類的實(shí)例上
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
抽象類
抽象類做為其它派生類的基類使用厨内。 它們一般不會直接被實(shí)例化。 不同于接口渺贤,抽象類可以包含成員的實(shí)現(xiàn)細(xì)節(jié)雏胃。 abstract關(guān)鍵字是用于定義抽象類和在抽象類內(nèi)部定義抽象方法。
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
抽象類中的抽象方法不包含具體實(shí)現(xiàn)并且必須在派生類中實(shí)現(xiàn)志鞍。 抽象方法的語法與接口方法相似瞭亮。 兩者都是定義方法簽名但不包含方法體。 然而固棚,抽象方法必須包含 abstract關(guān)鍵字并且可以包含訪問修飾符统翩。
abstract class Department {
constructor(public name: string) {
}
printName(): void {
console.log('Department name: ' + this.name);
}
abstract printMeeting(): void; // 必須在派生類中實(shí)現(xiàn)
}
class AccountingDepartment extends Department {
constructor() {
super('Accounting and Auditing'); // 在派生類的構(gòu)造函數(shù)中必須調(diào)用 super()
}
printMeeting(): void {
console.log('The Accounting Department meets each Monday at 10am.');
}
generateReports(): void {
console.log('Generating accounting reports...');
}
}
let department: Department; // 允許創(chuàng)建一個對抽象類型的引用
department = new Department(); // 錯誤: 不能創(chuàng)建一個抽象類的實(shí)例
department = new AccountingDepartment(); // 允許對一個抽象子類進(jìn)行實(shí)例化和賦值
department.printName();
department.printMeeting();
department.generateReports(); // 錯誤: 方法在聲明的抽象類中不存在
五:函數(shù)
1仙蚜,介紹
函數(shù)是JavaScript應(yīng)用程序的基礎(chǔ)。 它幫助你實(shí)現(xiàn)抽象層厂汗,模擬類委粉,信息隱藏和模塊。 在TypeScript里面徽,雖然已經(jīng)支持類艳丛,命名空間和模塊,但函數(shù)仍然是主要的定義 行為的地方趟紊。 TypeScript為JavaScript函數(shù)添加了額外的功能,讓我們可以更容易地使用碰酝。
2霎匈,函數(shù)
和JavaScript一樣,TypeScript函數(shù)可以創(chuàng)建有名字的函數(shù)和匿名函數(shù)送爸。 你可以隨意選擇適合應(yīng)用程序的方式铛嘱,不論是定義一系列API函數(shù)還是只使用一次的函數(shù)。
通過下面的例子可以迅速回想起這兩種JavaScript中的函數(shù):
// Named function
function add(x, y) {
return x + y;
}
// Anonymous function
let myAdd = function(x, y) { return x + y; };
在JavaScript里袭厂,函數(shù)可以使用函數(shù)體外部的變量墨吓。 當(dāng)函數(shù)這么做時,我們說它‘捕獲’了這些變量纹磺。 至于為什么可以這樣做以及其中的利弊超出了本文的范圍帖烘,但是深刻理解這個機(jī)制對學(xué)習(xí)JavaScript和TypeScript會很有幫助。
let z = 100;
function addToZ(x, y) {
return x + y + z;
}
3橄杨,函數(shù)類型
為函數(shù)定義類型
讓我們?yōu)樯厦婺莻€函數(shù)添加類型:
function add(x: number, y: number): number {
return x + y;
}
let myAdd = function(x: number, y: number): number { return x + y; };
我們可以給每個參數(shù)添加類型之后再為函數(shù)本身添加返回值類型秘症。 TypeScript能夠根據(jù)返回語句自動推斷出返回值類型,因此我們通常省略它式矫。
書寫完整函數(shù)類型
函數(shù)的類型只是由參數(shù)類型和返回值組成的乡摹。 函數(shù)中使用的捕獲變量不會體現(xiàn)在類型里。 實(shí)際上采转,這些變量是函數(shù)的隱藏狀態(tài)并不是組成API的一部分聪廉。
推斷類型
如果你在賦值語句的一邊指定了類型但是另一邊沒有類型的話,TypeScript編譯器會自動識別出類型
這叫做“按上下文歸類”故慈,是類型推論的一種板熊。 它幫助我們更好地為程序指定類型。
4惯悠,可選參數(shù)和默認(rèn)參數(shù)
TypeScript里的每個函數(shù)參數(shù)都是必須的邻邮。 這不是指不能傳遞 null或undefined作為參數(shù),而是說編譯器檢查用戶是否為每個參數(shù)都傳入了值克婶。 編譯器還會假設(shè)只有這些參數(shù)會被傳遞進(jìn)函數(shù)筒严。 簡短地說丹泉,傳遞給一個函數(shù)的參數(shù)個數(shù)必須與函數(shù)期望的參數(shù)個數(shù)一致。
function buildName(firstName: string, lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // ah, just right
JavaScript里鸭蛙,每個參數(shù)都是可選的摹恨,可傳可不傳。 沒傳參的時候娶视,它的值就是undefined晒哄。 在TypeScript里我們可以在參數(shù)名旁使用 ?實(shí)現(xiàn)可選參數(shù)的功能。
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // works correctly now
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // ah, just right
可選參數(shù)必須跟在必須參數(shù)后面肪获。 如果上例我們想讓first name是可選的寝凌,那么就必須調(diào)整它們的位置,把first name放在后面孝赫。
在TypeScript里较木,我們也可以為參數(shù)提供一個默認(rèn)值當(dāng)用戶沒有傳遞這個參數(shù)或傳遞的值是undefined時。 它們叫做有默認(rèn)初始化值的參數(shù)青柄。 讓我們修改上例伐债,把last name的默認(rèn)值設(shè)置為"Smith"。
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
let result2 = buildName("Bob", undefined); // still works, also returns "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result4 = buildName("Bob", "Adams"); // ah, just right
在所有必須參數(shù)后面的帶默認(rèn)初始化的參數(shù)都是可選的致开,與可選參數(shù)一樣峰锁,在調(diào)用函數(shù)的時候可以省略。 也就是說可選參數(shù)與末尾的默認(rèn)參數(shù)共享參數(shù)類型双戳。
function buildName(firstName: string, lastName?: string) {
// ...
}
和
function buildName(firstName: string, lastName = "Smith") {
// ...
}
共享同樣的類型(firstName: string, lastName?: string) => string虹蒋。 默認(rèn)參數(shù)的默認(rèn)值消失了,只保留了它是一個可選參數(shù)的信息拣技。
與普通可選參數(shù)不同的是千诬,帶默認(rèn)值的參數(shù)不需要放在必須參數(shù)的后面。 如果帶默認(rèn)值的參數(shù)出現(xiàn)在必須參數(shù)前面膏斤,用戶必須明確的傳入 undefined值來獲得默認(rèn)值徐绑。 例如,我們重寫最后一個例子莫辨,讓 firstName是帶默認(rèn)值的參數(shù):
function buildName(firstName = "Will", lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // okay and returns "Bob Adams"
let result4 = buildName(undefined, "Adams"); // okay and returns "Will Adams"
5傲茄,剩余參數(shù)
必要參數(shù),默認(rèn)參數(shù)和可選參數(shù)有個共同點(diǎn):它們表示某一個參數(shù)沮榜。 有時盘榨,你想同時操作多個參數(shù),或者你并不知道會有多少參數(shù)傳遞進(jìn)來蟆融。 在JavaScript里草巡,你可以使用 arguments來訪問所有傳入的參數(shù)。
在TypeScript里型酥,你可以把所有參數(shù)收集到一個變量里:
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
剩余參數(shù)會被當(dāng)做個數(shù)不限的可選參數(shù)山憨。 可以一個都沒有查乒,同樣也可以有任意個。 編譯器創(chuàng)建參數(shù)數(shù)組郁竟,名字是你在省略號( ...)后面給定的名字玛迄,你可以在函數(shù)體內(nèi)使用這個數(shù)組。
這個省略號也會在帶有剩余參數(shù)的函數(shù)類型定義上使用到:
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;
6棚亩,this
7蓖议,重載
六:泛型
略
七:枚舉
八:類型推導(dǎo)
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 45) {
console.log("Galloping...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
class Rhino extends Animal {
constructor() { super("Rhino"); }
}
class Employee {
private name: string;
constructor(theName: string) { this.name = theName; }
}
let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");
animal = rhino;
animal = employee; // 錯誤: Animal 與 Employee 不兼容.
class Person {
protected name: string;
constructor(name: string) { this.name = name; }
}
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name)
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // 錯誤
class Person {
protected name: string;
protected constructor(theName: string) { this.name = theName; }
}
// Employee 能夠繼承 Person
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
let john = new Person("John"); // 錯誤: 'Person' 的構(gòu)造函數(shù)是被保護(hù)的.
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
this.name = theName;
}
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // 錯誤! name 是只讀的.
class Octopus {
readonly numberOfLegs: number = 8;
constructor(readonly name: string) {
}
}
let passcode = "secret passcode";
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (passcode && passcode == "secret passcode") {
this._fullName = newName;
}
else {
console.log("Error: Unauthorized update of employee!");
}
}
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
alert(employee.fullName);
}
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
abstract class Department {
constructor(public name: string) {
}
printName(): void {
console.log('Department name: ' + this.name);
}
abstract printMeeting(): void; // 必須在派生類中實(shí)現(xiàn)
}
class AccountingDepartment extends Department {
constructor() {
super('Accounting and Auditing'); // 在派生類的構(gòu)造函數(shù)中必須調(diào)用 super()
}
printMeeting(): void {
console.log('The Accounting Department meets each Monday at 10am.');
}
generateReports(): void {
console.log('Generating accounting reports...');
}
}
let department: Department; // 允許創(chuàng)建一個對抽象類型的引用
department = new Department(); // 錯誤: 不能創(chuàng)建一個抽象類的實(shí)例
department = new AccountingDepartment(); // 允許對一個抽象子類進(jìn)行實(shí)例化和賦值
department.printName();
department.printMeeting();
department.generateReports(); // 錯誤: 方法在聲明的抽象類中不存在
// Named function
function add(x, y) {
return x + y;
}
// Anonymous function
let myAdd = function(x, y) { return x + y; };
let z = 100;
function addToZ(x, y) {
return x + y + z;
}
function add(x: number, y: number): number {
return x + y;
}
let myAdd = function(x: number, y: number): number { return x + y; };
function buildName(firstName: string, lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // ah, just right
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // works correctly now
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // ah, just right
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
let result2 = buildName("Bob", undefined); // still works, also returns "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result4 = buildName("Bob", "Adams"); // ah, just right
function buildName(firstName: string, lastName?: string) {
// ...
}
function buildName(firstName: string, lastName = "Smith") {
// ...
}
function buildName(firstName = "Will", lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // okay and returns "Bob Adams"
let result4 = buildName(undefined, "Adams"); // okay and returns "Will Adams"
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;