title: TypeScript 回顧整理
date: 2019/10/08 15:04:01
tags:
前端
TS
categories:
- 前端
Vue3.0 最近發布了pre-alpha版本,基本都是由TS編寫的,借此機會回顧一下TypeScript相關的概念和知識點
基礎用法
基本數據類型
JS中的原始基本數據類型:布爾值、數值、字符串、null
、undefined
以及 ES6 中的新類型 Symbol
boolean
布爾值是最基礎的數據類型,在 TypeScript 中,使用 boolean 定義布爾值類型:
let isDone: boolean = false;
注意,使用構造函數 Boolean 創造的對象不是布爾值:
let createdByNewBoolean: boolean = new Boolean(1);
// Type 'Boolean' is not assignable to type 'boolean'.
// 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible.
事實上 new Boolean() 返回的是一個 Boolean 對象:
let createdByNewBoolean: Boolean = new Boolean(1);
直接調用 Boolean 也可以返回一個 boolean 類型:
let createdByBoolean: boolean = Boolean(1);
在 TypeScript 中,boolean 是 JavaScript 中的基本類型,而 Boolean 是 JavaScript 中的構造函數, 不屬于基本類型。其他基本類型(除了 null 和 undefined)一樣。
number
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
// ES6 中的二進制表示法
let binaryLiteral: number = 0b1010;
// ES6 中的八進制表示法
let octalLiteral: number = 0o744;
let notANumber: number = NaN;
let infinityNumber: number = Infinity;
上述的二進制和八進制編譯為js之后都會轉為十進制。
string
let myName: string = 'Tom';
let myAge: number = 25;
let info: string = `Hello, i am ${myName} and i am ${myAge} years old`;
對于模板字符串來講,編譯為JS時會改為字符串拼接的形式
空值void
js中不存在void的概念,在ts中也只有聲明一個函數沒有返回值的時候才使用void(注意不是undefined!!!)
function alertName(): void {
alert('My name is Tom');
}
而如果要聲明一個void變量的話(無意義的操作),只能賦值為null或者undefined
let voidVal: void = undefined;
null和undefined
let u: undefined = undefined;
let n: null = null;
在TS中,undefined和null是所有類型的子類!這意味著你可以賦值給所有的類型一個null或者undefined
let num: number = undefined;
但是void類型就不行
任意值 Any
任意值(Any)用來表示允許賦值為任意類型
聲明為any類型的變量可以進行下面的操作:
let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;
any的屬性和方法
在任意值上訪問任何屬性都是允許的:
// 編譯通過
let anyThing: any = 'hello';
console.log(anyThing.myName);
console.log(anyThing.myName.firstName);
// 編譯通過
let anyThing: any = 'Tom';
anyThing.setName('Jerry');
anyThing.setName('Jerry').sayHello();
anyThing.myName.setFirstName('Cat');
聲明一個變量為任意值之后,對它的任何操作,返回的內容的類型都是任意值。
未聲明類型的變量
變量如果在聲明的時候,未指定其類型,那么它會被識別為any類型:
// 編譯通過
let something;
something = 'seven';
something = 7;
something.setName('Tom');
類型推斷
TypeScript 會在沒有明確的指定類型的時候推測出一個類型,這就是類型推論。
let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.
但是值得注意的是:
如果定義的時候沒有賦值,不管之后有沒有賦值,都會被推斷成 any 類型而完全不被類型檢查:
聯合類型Union Types
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
訪問聯合類型的屬性或者方法
當 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'.
聯合類型的變量在被賦值的時候,會根據類型推論的規則推斷出一個類型,此后再被賦值為第二種類型之后,訪問其不存在的屬性或者方法就會報錯
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length); // 5
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); // 編譯時報錯
// index.ts(5,30): error TS2339: Property 'length' does not exist on type 'number'.
類型守衛(Type Guards)與類型區分(Differentiating Types)
假設現在有如下兩個類型:
interface Fish {
swim(): void;
sayHi(): void;
}
interface Bird {
fly(): void;
sayHi(): void
}
對于聯合類型Fish | Bird
,引出來一個問題,當對于一個聯合類型來講,怎么確切的知道當前數據為哪個具體類型呢?
// 每一個成員訪問都會報錯
if(animal.swim) { // 報錯 animal類型未知 不可訪問swim方法
animal.swim();
}else {
animal.fly();
}
上例中由于類型未定,所以訪問每一個類型獨有的方法都會報錯,所以為了使其生效,只能使用類型斷言:
if((animal as Fish).swim) {
(animal as Fish).swim();
}else {
(animal as Bird).fly();
}
這里我們注意到,雖然在if語句里已經判斷了類型,但是在分支內部想調用特有方法時,還是需要再使用一次斷言(animal as Fish).swim();
,而TypeScript里的類型守衛機制可以使我們一旦檢查過類型,就能在之后的每個分支里清楚地知道變量的類型。
用戶自定義類型守衛
類型守衛主要分為:
使用類型判定
使用in操作符
typeof類型守衛
instanceof類型守衛
使用類型判定
要定義一個類型守衛,我們只要簡單地定義一個函數,它的返回值是一個類型謂詞:
function isFish(animal: Fish | Bird): animal is Fish {
// return !!(animal as Fish).swim;
return (animal as Fish).swim !== undefined;
}
在這個例子里,
animal is Fish
就是類型謂詞。 謂詞為parameterName is Type
這種形式,parameterName
必須是來自于當前函數簽名里的一個參數名。
每當使用一些變量調用isFish時,TypeScript會將變量縮減為那個具體的類型,只要這個類型與變量的原始類型是兼容的。
if(isFish(animal)) {
animal.swim(); // 調用類型判定之后 在這個if分支里 TS已經知道animal確定是Fish類型的了
// 所以不需要再使用類型斷言進行調用
}else {
// 同理 在這個分支里 已經知道是Bird類型了
animal.fly();
}
注意TypeScript不僅知道在if分支里pet是Fish類型; 它還清楚在else分支里,一定不是Fish類型,即一定是Bird類型。
使用in操作符
in操作符可以作為類型細化表達式來使用。
對于n in x
表達式,其中n
是字符串字面量或字符串字面量類型且x
是個聯合類型,那么true
分支的類型細化為有一個可選的或必須的屬性n
,false
分支的類型細化為有一個可選的或不存在屬性n
。
if("swim" in animal) {
animal.swim();
}else {
animal.fly();
}
typeof類型守衛
對于原始類型來說,可以不用那么麻煩去寫斷言,直接通過typeof就可以進行類型判定:
function getLength(s: string | number) {
if(typeof s === "string") {
return s.length;
}
return s.toString().length;
}
這些typeof類型守衛只有兩種形式能被識別:typeof v === "typename"
和typeof v !== "typename"
,"typename"
必須是"number"
,"string"
,"boolean"
或"symbol"
。 但是TypeScript并不會阻止你與其它字符串比較,語言不會把那些表達式識別為類型守衛。
instanceof類型守衛
instanceof類型守衛是通過構造函數來細化類型的一種方式。
abstract class Fish {
abstract swim(): void;
abstract sayHi(): void;
}
abstract class Bird {
abstract fly(): void;
abstract sayHi(): void
}
let animal: Fish | Bird;
if(animal instanceof Fish) {
animal.swim(); // 通過instanceof也可以進行區分
}else {
animal.fly();
}
instanceof的右側要求是一個構造函數,TypeScript將細化為:
此構造函數的prototype屬性的類型,如果它的類型不為any的話
構造簽名所返回的類型的聯合
以此順序。
交叉類型(Intersection Types)
交叉類型是將多個類型合并為一個類型。 這讓我們可以把現有的多種類型疊加到一起成為一種類型,它包含了所需的所有類型的特性:
interface Fish {
swim(): void;
sayHi(): void;
}
interface Bird {
fly(): void;
sayHi(): void
}
let strangeAnimal: Fish & Bird = {
fly(): void {
console.log("flying");
},
swim(): void {
console.log("swimming");
},
sayHi(): void {
console.log("hi~");
}
};
上例中的strangeAnimal的類型為Fish & Bird
,這個類型的對象同時擁有了這兩種類型的成員。這個變量既為Fish類型,同時也是Bird類型。
接口——對象的類型
TypeScript 中的接口是一個非常靈活的概念,除了可用于對類的一部分行為進行抽象以外,也常用于對「對象的形狀(Shape)」進行描述。一般首字母大寫.
基本用法
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25
};
此時約束的對象不允許缺少接口的定義的屬性也不允許多加屬性。
可選屬性
interface Person {
name: string;
age?: number;
}
let tom: Person = {
name: 'Tom'
};
此時age屬性可有可無,但是依舊不可以多加屬性
任意屬性
interface Person {
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
gender: 'male'
};
此時可以多加任意的屬性。
使用[propName: string]
定義了任意屬性取 string 類型的值。
需要注意的是,一旦定義了任意屬性,那么確定屬性和可選屬性的類型都必須是它的類型的子集:
只讀屬性
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
tom.id = 9527;
// index.ts(14,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.
只讀屬性只讀的約束存在于第一次給對象賦值的時候,而不是第一次給只讀屬性賦值的時候:
數組的類型
數組定義有幾種方法:
「類型 + 方括號」表示法
let fibonacci: number[] = [1, 1, 2, 3, 5];
number[]類型的數組中不允許出現第二種類型(如string)的數據,相應的,如果對數組進行操作進行新增時,也不允許加入其他類型的數據。
let fibonacci: number[] = [1, 1, 2, 3, 5];
fibonacci.push('8');
// Argument of type '"8"' is not assignable to parameter of type 'number'.
數組泛型
使用Array<elemType>
來表示數組
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
用接口表示數組(不常用)
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
注意這么寫之后,變量其實不是數組類型,如push等方法是不可使用的。 本質上僅僅是個類數組類型。
類數組
對于arguments 等類數組, 不能用普通的數組的方式來描述,而應該用接口
TS中的內置對象中,就使用了這種方法:
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}
上述的IArguments接口即規定了arguments的類型:
function sum() {
let args: IArguments = arguments;
}
函數的類型
聲明式定義
對于聲明式的函數定義,規定輸入參數的個數和類型即可以及輸出的類型即可。
function sum(x: number, y: number): number {
return x + y;
}
注意: 參數不得少于或者多余函數的規定
函數表達式
如果想通過表達式方式定義函數的話,可以直接寫為:
let mySum = function (x: number, y: number): number {
return x + y;
};
或者寫為箭頭函數形式:
let mySum = (x: number, y: number): number => {
return x + y;
};
但是值得注意的是,這塊的sum其實是有類型的,只不過沒有顯示定義而TS幫我們進行了類型推斷而已,如果要顯式的定義類型,需要寫為:
let mySum: (x: number, y: number) => number = (x: number, y: number): number => {
return x + y;
};
注意這邊的第一個=>
代表著函數的類型,規定了函數的返回值,而第二個=>
則是箭頭函數
接口表示函數
還可以使用接口表示函數:
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
函數的可選參數
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
可選參數必須接在必需參數后面
函數參數的默認值
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
函數的...rest參數(剩余參數)
function push(array: any[], ...items: number[]) {
items.forEach(function(item) {
array.push(item);
});
}
push([], 1, 2, 3);
這邊的...items代表1,2,3, items即為[1,2,3]為number[]類型
rest 參數只能是最后一個參數
函數的重載
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
}
return x.split('').reverse().join('');
}
let a: number = reverse(123);
let b: number = reverse("abc"); // 編譯報錯 Type 'string' is not assignable to type 'number' .
前2次函數定義代表傳入的是number返回的也會是number,傳入string返回的也是string,最后的才為函數的實現
重載時會從最前面的定義開始進行匹配,所以優先把最精確的寫在最前
類型斷言(Type Assertion)
用來手動指定一個值的類型
斷言語法
<類型>值
值 as 類型
在tsx中只能使用 值 as 類型的語法
可以使用類型斷言對聯合類型進行斷言,但是不可斷言聯合類型之外的類型:
function getLength(something: string | number): number {
if ( (something as string).length ) {
return (<string>something).length;
}
return something.toString().length;
}
對于上例來說,參數為string|number,這里的參數直接可以斷言為string,但是不可以斷言為string | number之外的類型
類型斷言并不是類型轉換
聲明文件
// TODO
待補充
內置對象
ECMAScript的內置對象
ECMAScript中一些內置對象如Boolean
、Error
、Date
、RegExp
等,在TS中可以直接定義:
let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
因為在 TypeScript 核心庫的定義文件中定義了這些內置對象。
DOM 和 BOM 的內置對象
例如Document
、HTMLElement
、Event
、NodeList
等。
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
// Do something
});
進階
類型別名
類型別名用來給一個類型起個新名字。
type Name = string;
let s: Name = "abc";
字符串字面量類型
字符串字面量類型用來約束取值只能是某幾個字符串中的一個。
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
// do something
}
handleEvent(document.getElementById('hello'), 'scroll'); // 沒問題
handleEvent(document.getElementById('world'), 'dbclick'); // 報錯,event 不能為 'dbclick'
// index.ts(7,47): error TS2345: Argument of type '"dbclick"' is not assignable to parameter of type 'EventNames'.
元組
數組合并了相同類型的對象,而元組(Tuple)合并了不同類型的對象。
let tom: [string, number] = ['Tom', 25];
基本操作不贅述,值得注意的是,元組支持越界,但是越界時新增的元素必須是前面規定的那些類型之一
let tom: [string, number];
tom = ['Tom', 25];
tom.push('male'); // 越界時允許push的類型 本例中為 string | number
tom.push(true); // 編譯報錯
枚舉
枚舉(Enum)類型用于取值被限定在一定范圍內的場景,比如一周只能有七天,顏色限定為紅綠藍等。
枚舉成員會被賦值為從 0 開始遞增的數字,同時也會對枚舉值到枚舉名進行反向映射
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true
給枚舉手動賦值
枚舉可以在初始化的時候手動為其每一項賦值,未被賦值的成員會上一個枚舉項遞增。
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
本例中Tue開始就未賦值,所以會接著上一個枚舉項即Mon開始接著遞增。
賦值時允許多個項的值相等,但是會造成覆蓋情況!
對于上述的覆蓋情況,舉個例子解釋一下:
enum Colors = {red = 1, blue = 1, green};
會被編譯為:
var Colors ;
(function (Colors ) {
Colors [Colors ["red"] = 1] = "red";
Colors [Colors ["blue "] = 1] = "blue ";
Colors [Colors ["green"] = 2] = "green";
})(Colors || (Colors = {}));
實際最后的Colors為:
{
1: "blue",
2: "green",
blue: 1,
red: 1,
green: 2
}
可以看到,對于index的訪問方式來說,由于下標相同,之前的1: red
已經被覆蓋為1: blue
,所以要避免枚舉值賦值重復。
枚舉的計算所得項
對于上述的
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
即為常數項的枚舉,在枚舉中也可以使用計算所得項:
enum Color {Red, Green, Blue = "blue".length};
但是要注意的是:如果緊接在計算所得項后面的是未手動賦值的項,那么它就會因為無法獲得初始值而報錯
enum Color {Red = "red".length, Green, Blue};
// index.ts(1,33): error TS1061: Enum member must have initializer.
// index.ts(1,40): error TS1061: Enum member must have initializer.
詳見中文手冊
常數枚舉
注意和上面的常數項不是一個東西,常數枚舉值得是通過const
定義的枚舉,即:
使用const enum
定義的即為常數枚舉,常數枚舉會在編譯階段被刪除,并且不能包含計算成員。
const enum Directions {
Up,
Down,
Left,
Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
編譯為JS后代碼為:
var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
外部枚舉
指的是通過declare enum
定義的枚舉
declare enum Directions {
Up,
Down,
Left,
Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
編譯為:
var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
declare 定義的類型只會用于編譯時的檢查,編譯結果中會被刪除。
另外如果搭配const
定義,會被編譯為常數枚舉。
TS中的類
public private 和 protected
TypeScript 可以使用三種訪問修飾符(Access Modifiers),分別是 public
、private
和 protected
。
constructor 設為private和protected
防止類被繼承(即final class)且不能實例化的話,在TS中要講其constructor設為private
class Animal {
public name;
private constructor (name) {
this.name = name;
}
}
class Cat extends Animal {
constructor (name) {
super(name);
}
}
let a = new Animal('Jack');
// index.ts(7,19): TS2675: Cannot extend a class 'Animal'. Class constructor is marked as private.
// index.ts(13,9): TS2673: Constructor of class 'Animal' is private and only accessible within the class declaration.
而要類只不能實例話還可以被繼承的話,要使用protected修飾constructor
class Animal {
public name;
protected constructor (name) {
this.name = name;
}
}
class Cat extends Animal {
constructor (name) {
super(name);
}
}
let a = new Animal('Jack');
// index.ts(13,9): TS2674: Constructor of class 'Animal' is protected and only accessible within the class declaration.
此外修飾符還可以使用在構造函數參數中,等同于類中定義該屬性,使代碼更簡潔:
class Animal {
// public name: string;
public constructor (public name) {
this.name = name;
}
}
抽象類
TS中的抽象類基本和Java一致,不多贅述:
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
let a = new Animal('Jack');
類與接口
接口還可以對類的一部分行為進行抽象
interface Alarm {
alert();
}
interface Light {
lightOn();
lightOff();
}
class Car implements Alarm, Light {
alert() {
console.log('Car alert');
}
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}
補充一點: TS中接口可以繼承類
// TS中接口可以繼承類:
abstract class Point {
x: number;
y: number;
abstract showPoint(): void;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {
x: 1, y : 1, z: 1,
showPoint(): void {
console.log("");
}
};
此外接口也可以繼承接口
函數的屬性和方法
函數可以擁有自己的屬性和方法:
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
// 這里的counter既是一個函數,也擁有自己的屬性和方法
泛型
泛型的概念不再贅述,來看基本語法:
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray<string>(3, 'x'); // ['x', 'x', 'x']
泛型約束
在函數內部使用泛型變量的時候,由于事先不知道它是哪種類型,所以不能隨意的操作它的屬性或方法
function getLength<T>(x: T): number() {
return x.length;
}
// error TS2339: Property 'length' does not exist on type 'T'.
上述例子中由于T的類型不明,所以無法訪問length屬性。
這時可以對泛型進行約束,只允許這個函數傳入那些包含 length 屬性的變量,即泛型約束:
interface LengthAble {
length: number;
}
function getLength<T extends LengthAble>(x: T): number {
return x.length;
}
另外泛型之間也可以相互約束,比如在function fn<T extends U, U>(x: T, y: U) {}
,強制要求了前一個參數的類型繼承自后一個類型。
泛型接口
泛型也可以應用在接口上:
interface CreateArrayFunc {
<T>(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc;
createArray = function<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
也可以直接將泛型寫到接口上:
interface CreateArrayFunc<T> {
(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc<any>;
createArray = function<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
泛型類
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
泛型參數的默認類型
TS2.3以后新增了一個泛型參數的默認類型,使用泛型時沒有在代碼中直接指定類型參數,從實際值參數中也無法推測出時,會采用這個默認類型
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
聲明合并
如果定義了兩個相同名字的函數、接口或命名空間等,那么它們會合并成一個類型
函數合并
函數的重載就是函數的合并
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
接口的合并
- 屬性合并:
interface Alarm {
price: number;
}
interface Alarm {
weight: number;
}
等價于:
interface Alarm {
price: number;
weight: number;
}
合并的屬性的類型必須是唯一的
interface Alarm {
price: number;
}
interface Alarm {
price: number; // 雖然重復了,但是類型都是 `number`,所以不會報錯
weight: number;
}
interface Alarm {
price: number;
}
interface Alarm {
price: string; // 類型不一致,會報錯
weight: number;
}
// index.ts(5,3): error TS2403: Subsequent variable declarations must have the same type. Variable 'price' must be of type 'number', but here has type 'string'.
- 方法的合并
內部方法的合并和函數的合并原則相同
interface Alarm {
price: number;
alert(s: string): string;
}
interface Alarm {
weight: number;
alert(s: string, n: number): string;
}
等價于:
interface Alarm {
price: number;
weight: number;
alert(s: string): string;
alert(s: string, n: number): string;
}