const a: number = 1;
const b: string = '1';
const c: number[] = [1, 2, 3]; const d: Array<number> = [1, 2, 3]; const e: any[] = [1, '2', true];
const f: boolean = true;
const g: object = {};
經常使用於組合類型java
const h: number | undefined;
const i: null;
可爲數組中的每一個參數定義相對應的類型node
const j: [number, string] = [1, '2'];
enum err { first = 3, 'second', } const k: e = err.first; console.log(g); // 4
tipswebpack
- 若是未賦值的上一個值是數字,那麼這個未賦值的值就是上一個值 +1
- 若是未賦值的上一個值未賦值,那麼輸出的就是它的下標
- 若是未賦值的上一個值是非數字,那麼必須賦值
指定方法類型,表示沒有返回值,方法體中不能有return
web
function add(): void { console.log('add'); } // 若是方法體有返回值,能夠加上返回值的類型 function delete: string { return 'delete'; }
其餘類型(包括undefind和null)的子類型,表明從不會出現的值編程
let o: never; o = (() => { throw new Error('error msg'); })();
讓參數能夠是任何一種類型數組
let p: any = 1; p = '2'; p = true;
function add(): vide {}
function getUserInfo(name: string, age?: number, school: string = '哈佛大學'): string { return `name: ${name}, age: ${age || '年齡不詳'}, school: ${string}`; }
tips
?
表明這個參數可不傳,不傳即爲undefined
,也可定義默認值
function sum(a: number, b: number, ...arr: number[]): number { let sum: number = a + b; arr.forEach(i => { sum += i; }); console.log(arr); // [3, 4, 5] return sum; } console.log(sum(1, 2, 3, 4, 5)); // 15
function reload(name: string): void {} function reload(age: number): void {} function reload(info: any): any { if (typeof(info) === 'string') { console.log(`個人名字是: ${info}`); } else if (typeof(info) === 'number') { console.log(`個人年齡是: ${info}`); } } reload('Clearlove'); // 個人名字是Clearlove reload(18); // 個人年齡是18
function reload(name: string): void function reload(name: string, age?: number): void function reload(name: any, age? number): any { if (age) { console.log(`個人名字是: ${name}, 今年${age}歲!!`); } else { console.log(`你們好,個人名字是: ${name}`); } } reload('Clearlove'); // 你們好,個人名字是Clearlove reload('Clearlove', 18); // 個人名字是Clearlove, 今年18歲!!
tips瀏覽器
- 被重載的函數,是沒有函數體的,能夠根據參數的類型走其中一個方法並判斷參數
- 函數的重載與返回值類型無關
- 函數重載的做用:是一種參數校驗功能,在進行函數調用時,會對參數進行檢查,只有傳人的參數類型、順序、個數和重載的函數的參數相同時,才能調用成功,不然報錯
class Person { // 私有變量 private name: string; // 構造函數 constructor(name: string) { this.name = name; } getName(): string { return this.name; } setName(name: sring): void { this.name = name; } } const myBoy = new Person('Clearlove'); console.log(myBoy.getName()); // Clearlove myBoy.setName('test');
class Son extends Person { // 靜態屬性 public static age: number = 18; // 學校 public school: string; constructor(name: string, school: string) { // 訪問派生類的構造函數前,必須調用「super」,初始化父類構造函數,並把參數傳給父類 super(name); this.school = school; } // 靜態方法 static run(name: string): string { return `${name}在跑步,他是年齡是${this.age}`; } } const son = new Son('Clearlove', '清華大學'); son.setName('Test'); console.log(son); console.log(Son.run('Clearlove')); // Clearlove在跑步,他的年齡是18 console.log(Son.age); // 18
tipsapp
- public 在當前類、子類和類之外均可以訪問
- protected 在當前類、子類內部均可以訪問,類外部沒法訪問
- private 在當前類內部能夠訪問,子類和類外部沒法訪問
- 屬性不加修飾符,默認都是public
經過抽象方法/方法重載,實現多態。多態的做用是用來定義標準
// 抽象父類ide
abstract class Animal { // 私有屬性 private name: string; constructor(name: string) { this.name = name; } // 抽象成員: 方法 abstract eat(): any; // 抽象成員: 屬性 protected abstract ages: number; sleep(): void { console.log(`${this.name}在睡覺`); } } class Cat extends Animal { ages: number = 2; constructor(name: string) { super(name); } // 非抽象類: Cat 不會自動實現繼承自: Animal類的抽象方法: eat, 必須手動定義父類中的抽象方法,着就是多態 eat(): string { return '貓吃魚'; } sleep(): string { return '貓在睡覺'; } } const cat = new Cat('Tom'); cat.sleep();
tips函數
- 抽象類沒法 實例化
- 非抽象類繼承父類時,不會自動實現 來自父類的抽象成員,必須手動定義 父類中的成員,不然會報錯
- 抽象成員包含 屬性 和 方法
在面向對象的編程中,接口是一種規範的定義,它定義了行爲和動做的規範。
在程序設計裏面,接口起到了一種限制和規範的做用。
接口定義了某一批類所需遵照的規範,接口沒必要關心這些類的內部狀態數據,也不關心這些類裏方法的實現細節,它只規定這批類必須提供某些方法,提供這些方法的類就能夠知足實際須要。ts中的接口相似java,同時還增長了更靈活的接口類型,包括屬性、函數、可索引和類等。
interface InterfaceName { first: string; second?: string; } function logParam(name: InterfaceName): viod { console.log(name.first, name.second. 'test'); } const obj = { first: '1', second: '2'. three: '3' }; logParam({ first: '1', second: '2'. three: '3' }); // 報錯,只能傳接口定義的值 logParam(obj);
tips
用變量存儲數據,這樣能夠傳入定義的接口外的值,不然若是直接傳入對象中無接口定義的值會報錯
#### 函數類型接口
對函數傳入的參數類型,以及返回值類型進行約束,可批量進行約束
interface keyMap { (key: string, value: string): string; } let logKeyMap: keyMap = fucntion (key: string, value: string): string { return key + value; } console.log(logKeyMao('key', 'value'));
tips
接口只對傳入的參數的類型和參數的個數進行約束,不對參數名稱進行約束
interface Arr { [index: number]: string; } let test: Arr = ['123'];
interface Obj { [index: string]: string; } let test: Obj = { name: 'Clearlove' };
tips
- 對 數組 進行約束,index必須是 number類型
- 對 對象 進行約束,index必須是 string類型
- 索引簽名參數類型必須爲string或者number
interface Ainmal { name: string; eat(): void; } calss Dogs implements Animal { name: string; constructor(name: string) { this.name = name; } eat() {} }
interface Dog { ear(): void; } interface Persons entexds Dog { work(): void; } class Cat { code() { console.log('貓在敲代碼'); } } class SuperMan extends Cat implements Persons { eat(): void { console.log('eat'); } work(): void { console.log('work'); } } const man = new SuperMan(); man.code();
tips
類接口會對類的 屬性 和 方法進行約束,相似非抽象類繼承類時必須實現某些方法和屬性,但對於屬性和方法的類型約束更加嚴格。除了方法 void類型 可被 從新定義外,其餘屬性或方法的類型定義須要和接口保持一致。
軟件工程中,咱們不只要建立一致的、定義良好的API, 同時也要考慮重用性。
組件不只可以支持當前的數據類型,同時也能支持將來的數據類型,在建立大型系統時爲你提供了十分靈活的功能。
泛型就是解決 類、接口、方法 的 複用性,以及對不特定數據類型的支持。
要求:傳入的參數和返回的參數一致
function getDate<T>(value: T): T { return value; } const val = getDate<number>(123); console.log(val);
tips
這裏的T 能夠改爲其餘任意值,但定義的值和傳入的參數以及返回的值是同樣的。通常默認寫法是T,也是業內規範的選擇。
class MainClass<T> { public list: T[] = []; add(value: T): void { this.list.push(value); } min(): T { let minNum = this.list[0]; for(let i = 0; i < this.list.length; i++) { minNum < this.list[i] ? minNum : this.list[i]; } return minNum; } } // 實例化類,指定類的T的類型是number const minClass = new MainClass<number>(); minClass.add(1); minClass.add(2); minClass.add(3); console.log(minClass.min()); // 實例化類,並指定了類的T的類型是string,則其方法的傳參和返回值都是string類型 let minClass2 = new MainClass<string>(); minClass2.add('1'); minClass2.add('2'); minClass2.add('3'); console.log(minClass2.min());
interface Config { // 規範參數類型和返回值類型 <T>(value: T): T; } let getDate: Config = function <T>(value: T): T { return value; } const data = getData<string>('123'); console.log(data);
interface Config<T> { // 規範參數和返回值類型 (value: T): T } // 接口方法 function getData<T>(value: T): T { return value; } // 使用接口 let myGetData: Config<string> = getData; consoie.log(myGetData('123'));
tips
接口的泛型只針對函數類型的接口
class User { username: string | undefined; password: string | undefined; constructor(params: { usermame: string | undefined, password?: string | undefined }) { this.username = params.username; this.password = params.password; } } class Db<T> { add(user: T): boolean { console.log(user); return true; } updated(user: T, id: number): boolean { console.log(user, id); return true; } } let user = new User({ username: 'Clearlove' }); user.password = '123'; let db = new Db<User>(); db.add(user); db.updated(user, 1);
tips
類的參數名和類型都作了約束
內部模塊成爲命名空間,外部模塊簡稱爲模塊,模塊在起自身的做用域裏執行,而不是在全局做用域。
定義在一個模塊裏的變量、函數、類等在模塊外是不可見的,除非你明確的使用export
形式導出它們。
對應的,若是想使用其餘模塊導出的變量、函數、類等,須要導入它們,可使用import
。
// modules/db.ts function getData(): any[] { console.log('獲取數據'); return [ { userName: '張三' }, { userName: '李四 } ]; } // 一個模塊可使用屢次 export { getData }; // 一個模塊只能使用一次 export default getData;
import { getData as getDbData } from './modules/db'; import getDbData from './modules/db'; getDbData();
tips
瀏覽器中不能直接使用,可在node
和webpack
的環境中調試
在代碼量較大的狀況下,爲了不各類變量命名衝突,可將類似功能的函數、類、接口等放置到命名空間內。TypeScript的命名空間能夠將代碼包裹起來,只對外部暴露須要在外部訪問的對象。
命名空間和模塊的區別:
// modules/Animal.ts export namespace A { interface Animal { name: string; say(): void; } export class Dog implements Animal { name: string; constructor(name: string) { this.name = name; } say() { console.log(`我是${this.name}`); } } } export namespace B { interface Animal { name: string; eat(): void; } export class Dog implements Animal { name: string; constructor(name: string) { this.name = name; } say() { console.log(`Hello, my name is ${this.name}`); } } }
import {A, B} from './modules/Animal'; const dog = new A.Dog('小馬'); dog.say();
裝飾器本質上是一種特殊的函數,被應用在於:
因此應用裝飾器其實很想是組合一系列函數,相似於高階函數和類。
裝飾器的語法十分簡單,只須要在想使用的裝飾器前面加上@
符號,裝飾器就會被應用到目標上:
function simpleDecorator() { console.log('i am a decorator!'); } @simpleDecorator class A {}
一共有5種裝飾器能夠被咱們使用:
@classDecorator class Bird { // 屬性裝飾器 @propertyDecorator name: string; // 方法裝飾器 @methodDecorator fly ( // 參數裝飾器 @parameterDecorator meters: number ) {} // 訪問器裝飾器 @accessorDecorator get egg() {} }
裝飾器只在解析執行時應用一次,例如:
function f() { console.log('apply decotator'); return true; } @f class A {} // output: apply decorator
這裏的代碼會在終端中打印apply decorator
,即便咱們其實並無使用類A
不一樣類型的裝飾器執行順序是明肯定義的:
例如:
function f(key: string) { console.log(`evaluate: ${key}`); return function() { console.log(`call: ${key}`); } } @f('class Decorator') class A { @f('Static Property') static prop?: number; @f('Static Method') static method(@f('Static Methos Parameter') foo) {} constructor(@f('Constructor Parameter') foo) {} @f('Instance Method') method(@f('Instance Mthdos Parameter') foo) {} @f('Instance Propterty') prop?: number; } // evaluate Inastance Method // evaluate Inastance Method Parameter // call: Instace Method Parameter // call: Instace Method // evaluate Inastance Property // call: Inastance Property // evaluate Static Property // call: Static Property // evaluate Static Method // evaluate Static Method Parameter // call: Static Method Parameter // call: Static Method // evaluate: Class Decorator // evaluate: Constructor Decorator // call: Constructor Decorator // call: Class Decorator
你也許會注意到,執行實例屬性prop
晚於實例方法method
。然而執行靜態屬性static prop
早於靜態方法static method
。
這是由於對於屬性/方法/訪問器 裝飾器而言,執行順序取決於它們的聲明順序。
然而,同一方法中不一樣參數的裝飾器的執行順序是相反的,最後一個參數的裝飾器會被先執行:
function f(key: string) { console.log(`evaluate: ${key}`); return function () { console.log(`call: ${key}`); } } class A { method() { @f('Parameter Foo') foo, @f('Parameter Bar') bar } {} } // evaluate Parameter Foo // evaluate Parameter Bar // call Parameter Bar // call Parameter Foo
能夠對同一個目標應用多個裝飾器,它們的組合順序爲:
例如:
function f(key: string) { console.log(`evaluate: ${key}`); return function () { console.log(`call: ${key}`); } } class A { @f('Outer Method') @f('Inner Method') method() {} } // evaluate: Outer Method // evaluate: Inner Method // call: Inner Method // call: Outer Method
應用於類構造器,其參數類的構造函數
function addAge(args: number) { return function(target: Function) { target.prototype.age = args; } } @addAge(18) class Hello { name: string; age: number; constructor() { console.log('Hello World'); this.name = 'Clearlove'; } } console.log(Hello.prototype.age); // 18 const hello = new Hello(); console.log(hello.age); // 18
它會被應用到方法的屬性描述符上,能夠用來監視、修改或者替換方法定義。
方法裝飾器會在運行時傳入下列三個參數:
function addAge(constructor: Function) { constructor.prototype.age = 18; } function method(tagrget: any, propertyKey: string, descriptor: PropertyDescriptor) { console.log(target); console.log(`prop: ${propertyKey}`); console.log(`desc: ${JSON.stringify(descriptor)}` + '\n\n'); } @addAge class Hello { name: string; age: number; constructor() { console.log('Hello World'); this.name = 'Clearlove'; } @method hello(): string { return 'Instance method'; } @method static sayHello(): string { return 'Static method'; } } // Hello {hello: [Function]} // prop: hello // desc: {"writabel": true, "enumerabel": true, "configurable": true} // { [Function: Hello] seyHello: [Function] } // prop: sayHello /// desc: {"writabel": true, "enumerabel": true, "configurable": true}
假如修飾的是hello
這個實例方法,第一個參數就是原型對象,也就是Hello.prototype。
加入修飾的是sayHello
這個靜態方法,第一個參數就是構造器constructor
。
訪問器裝飾器應用於訪問器的屬性描述符,可用於觀察、修改或替換訪問者的定義。
訪問器不能再聲明文件中使用,也不能在任何其餘環境上下文中使用(例如在聲明類中)。
訪問器裝飾器表達式會在運行時看成函數被調用,傳人下列三個參數:
例子:
function configurabele(value: boolean) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { descriptor.configurabel = value; } } class Point { private _x: number; private _y: number; constructor(x: number, y: number) { this._x = x; this._y = y; } @configurable(false) get _x() { return this_.x; } @configurable(false) get _y() { return this._y; } }
屬性裝飾器表達式會在運行時看成函數被調用,傳人下列2個參數:
function log(target: any, propertyKey: string) { let value = target[properttKey]; const getter = function () { console.log(`Getter for ${propertyKey} returned ${value}`); return value; } const setter = function (newVal) { console.log(`Set ${propertyKey} to ${newVa;}`); value = newVal; } if (delete this[propertyKey]) { Object.defineProperty(target, propertyKey, { get: getter, set: setter, enumerable: true, configurable: true }); } } class Calculator { @log public num: number; square() { this.num * this.num; } } const cal = new Calculator(); cal.num = 2; console.log(cal.square); // Set num to 2 // Getter for num returned 2 // Getter for num returned 2 // 4
參數裝飾器表達式會在運行時看成函數被調用,傳入下列3個參數:
const parseConf = []; // 在函數調用前執行格式化操做 function parseFunc(target: any, name, descriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args: any[]) { for (let index = 0; index < parseConf.length; index++) { const type = parseConf[index]; console.log(type); switch (type) { case 'number': args[index] = Number(args[index]); break; case 'string': args[index] = String(args[index]); break; case 'boolean': args[index] = String(args[index]) === 'true'; break; } return originalMethod.apply(this, args); } }; return descriptor; } // 向全局對象中添加對應的格式化信息 function parse(type) { return function (target, name, index) { parseConf[index] = type; console.log('parseConf[index]:', type); }; } class Modal { @parseFunc public addOne(@parse('number') num) { console.log('num:', num); return num + 1; } } let modal = new Modal(); console.log(modal.addOne('10')); // 11