TypeScript筆記。一些特性總結(jié)
新的數(shù)據(jù)類型
元祖(Tuple)
屬于數(shù)組的一種,數(shù)組合并了相同類型,元祖合并了不同類型
let tom: [string, number] = ['tom', 25]
枚舉類型(enum)
將一些事物的狀態(tài)和數(shù)值對(duì)應(yīng)起來(lái),盡量用自然語(yǔ)言中的含義清楚的單詞來(lái)表示它的每一個(gè)值。
enum 枚舉名 {
標(biāo)識(shí)符[=整形常數(shù)]
標(biāo)識(shí)符[=整形常數(shù)]
標(biāo)識(shí)符[=整形常數(shù)]
}
enum Flag {
success = 1,
error = 2
}
let s:Flag = Flag.success
console.log(s) // 1
enum Flag {
success ,
error
} // 如果標(biāo)識(shí)符沒(méi)有對(duì)映數(shù)值,這位下標(biāo)值
enum Color { // 用具體單詞表示表示數(shù)字狀態(tài)的字符串
blue,
red = 3,
'orange'
}
console.log(Color['red'])//3
console.log(Color.orange) //4
consoloe.log(Color[0]) //blue 枚舉值到枚舉名也會(huì)進(jìn)行反向映射
應(yīng)用,比如說(shuō)錯(cuò)誤的HTTP狀態(tài)碼
任意類型(any)
void類型
void表示沒(méi)有任何類型,一般將用于定義方式的時(shí)候沒(méi)有返回值
never類型
基本上用不到
函數(shù)重載
java中方法的重載,指的是兩個(gè)或者兩個(gè)以上同名函數(shù),但它們的參數(shù)不一樣,這時(shí)會(huì)出現(xiàn)函數(shù)重載的情況。
-
typescript中的重載,通過(guò)為同一個(gè)函數(shù)提供多個(gè)函數(shù)類型定義來(lái)試下多種功能的母的。
ES5不支持后寫的同名函數(shù)會(huì)覆蓋,TS支持
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(''); } }重復(fù)定義了多次函數(shù),前面兩次都是定義,string返回string,number返回number;
最后一次是函數(shù)實(shí)現(xiàn)。
typescript類
實(shí)例修飾符
TS可以使用三種修飾符(modifiers)public,private,protected,修飾實(shí)例屬性可用范圍。
-
public修飾的屬性或方法是公有的,可以在任何地方被訪問(wèn)到,默認(rèn)都是public,在類里面,之類,類外部都可以訪問(wèn) -
protected修飾的屬性或方法是受保護(hù)的,在類里面、子類里面可以訪問(wèn),在類外部沒(méi)法訪問(wèn) -
private修飾的屬性或方法是私有的,只能在當(dāng)前類使用。
靜態(tài)屬性 靜態(tài)方法
staticES6 ,靜態(tài)只能調(diào)用靜態(tài)
多態(tài)
- 父類定義一個(gè)方法不去實(shí)現(xiàn),讓繼承它的子類去實(shí)現(xiàn),每一個(gè)子類有不同的表現(xiàn)。
- 多態(tài)屬于繼承
class Animal {
name: string;
constructor(name:string){
this.name = name
}
eat() {
console.log('吃的東西')
}
}
class Dog extends Animal {
constructor(name:string) {
super(name)
}
eat() {
return this.name + '吃糧食'
}
}
class Cat extends Animal {
constructor(name:string) {
super(name)
}
eat(){
return this.name + '吃老鼠'
}
}
抽象類
typescript中的抽象類,它是提供其他類繼承的基類,不能被實(shí)例化。
用
abstract關(guān)鍵字定義抽象類和抽象方法,抽象類中的抽象方法不包含具體實(shí)現(xiàn)并且必須在派生類中實(shí)現(xiàn)abstract抽象方法只能放在抽象類里面抽象類和抽象方法用來(lái)定義標(biāo)準(zhǔn)
抽象類的子類必須實(shí)現(xiàn)抽象類里面的抽象方法
abstract class Animal {
public name;
public constructor(name) {
this.name = name
}
abstract sayHi()
}
class Cat extends Animal {
public sayHi() {
console.log(`Tom, My name is ${this.name}`)
}
}
let car = new Cat('Tom')
interface接口
在面向?qū)ο蟮木幊陶Z(yǔ)言中,接口是一種規(guī)范的定義,它定義里行為和動(dòng)作規(guī)范,在程序設(shè)計(jì)里面,接口起到一種限制和規(guī)范的作用,接口定義了某一批類所需要遵守的規(guī)范。它是對(duì)行為的抽象,而具體如何行動(dòng)需要由類去實(shí)現(xiàn)。
TypeScript中的接口可以用于
- 對(duì)類的一部分行為進(jìn)行抽象
- 對(duì)對(duì)象的形狀進(jìn)行描述
屬性類接口
interface Fullname {
firstName:string;
secondName:string;
}
function printName(name:Fullname){
console.log(name.firstname + '-' + name.secondName)
}
//1. 外部包含接口就可以
const obj = {
age:20,
firstName: 'Tommy',
secondName: 'Shen',
}
printName(obj)
//2.嚴(yán)格遵守接口
printName({
firstName: 'Tommy',
secondeName: 'Shen'
})
//3.接口可一批量對(duì)映多個(gè)函數(shù)
可選屬性
interface Fullname {
firstName:string;
secondName?:string;
}
例子
interface Config {
method:string;
url:string;
data?:string;
headers?: any;
dataType:string;
}
function ajax<T>(config:Config):Promise<T> {
return new Promise((resolve, reject) => {
let request = new XMLHttpRequest()
request.open(config.method, config.url, true)
for(let key in config.headers){
let value = Headers[key]
request.setRequestHeader(key, value)
}
request.onreadystatechange = () => {
if(request.readyState === 4){
if(request.status >= 200 && request.status < 300){
if(config.dataType === 'JSON') {
resolvs.call(undefined, JSON.parse(request.responseText))
}else{
resolve.call(undefined, request.responseText)
}
}else if(request.status >= 400) {
reject.call(undefined, request.responseText)
}
}
}
})
}
可索引接口:數(shù)組 對(duì)象的約束
//對(duì)數(shù)組的約束
interface UserArr {
[index:number]:string
}
//對(duì)象的約束
interface userObj {
[index:string]:string
}
//類類型的接口 和 抽象類有點(diǎn)相似
interface Animal {
name:string;
eat(str:string):void
}
class Dog implement Animal {
name:string;
constructor(name:string){
this.name = name
}
eat(){
console.log(this.name + '吃肉肉')
}
}
const d = new Dog('小黑')
接口擴(kuò)展:接口可以繼承接口
interface Animal {
eat():void;
}
interface Person extends Animal{
work():void
}
class Web implements Person {
public name:string;
constructor(name:string){
this.name = name
}
eat(){
console.log(this.name + '吃噠面王')
}
work(){
console.log(this.name + '寫代碼')
}
}
類與接口(為什么有了抽象類還需要接口)
implement是面向?qū)ο笾械囊粋€(gè)重要概念,一個(gè)類只能繼承自另一個(gè)類;但不同類之間有一些共有的特性,這時(shí)候就可以把特性提取成接口,一個(gè)類可以實(shí)現(xiàn)多個(gè)接口,大大提高了面向?qū)ο蟮撵`活性。
接口繼承類 (重要 TS特有!?。。?/h5>
常見(jiàn)的面向?qū)ο笳Z(yǔ)言中,接口是不能繼承類的,但是在TS中卻是可以的
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
interface Pointed3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
為什么TypeScript會(huì)支持接口繼承類呢?
實(shí)際上,在生命class Point時(shí),除了會(huì)創(chuàng)建一個(gè)名為Point的類之外,同時(shí)也創(chuàng)建了一個(gè)名為Point的類型
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
interface PointInstanceType {
x: number;
y: number;
}
// 等價(jià)于 interface Point3d extends PointInstanceType
interface Point3d extends Point {
z: number;
}
所以「接口繼承類」和「接口繼承接口」沒(méi)有本質(zhì)的區(qū)別
值得注意的是,PointInstanceType 相比于 Point,缺少了 constructor 方法,這是因?yàn)槁暶?Point 類時(shí)創(chuàng)建的 Point 類型是不包含構(gòu)造函數(shù)的。另外,除了構(gòu)造函數(shù)是不包含的,靜態(tài)屬性或靜態(tài)方法也是不包含的(實(shí)例的類型當(dāng)然不應(yīng)該包括構(gòu)造函數(shù)、靜態(tài)屬性或靜態(tài)方法)。
總結(jié): 聲明Point類時(shí)創(chuàng)建的Point類型只包含其中的實(shí)例屬性和實(shí)例方法
同樣的,在接口繼承類的時(shí)候,也只會(huì)繼承它的實(shí)例屬性和實(shí)例方法。
泛型
泛型: 軟件工程中,我們不僅要?jiǎng)?chuàng)建一致的定義良好的API,同時(shí)也要考慮可重用性。組件不僅能夠支持當(dāng)前的數(shù)據(jù)類型,同時(shí)也能支持未來(lái)的數(shù)據(jù)類型
通俗理解:泛型就是解決 類 接口 方法的復(fù)用性、以及對(duì)不特定數(shù)據(jù)類型的支持
//泛型:可以支持不特定的數(shù)據(jù)類型
//要求:傳入的參數(shù)和返回的參數(shù)一致
//T表示泛型,具體什么類型是調(diào)用這個(gè)方法的時(shí)候決定的
function getData<T>(value:T):T {
return value
}
//都是any 失去了類型檢查的意義
function createArray(length: number, value: any): Array<any> {
let result = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
//使用泛型,調(diào)用的時(shí)候傳入數(shù)據(jù)類型
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']
多個(gè)類型參數(shù)
//定義泛型的時(shí)候,可以一次定義多個(gè)類型參數(shù)
function swap<T, U>(tuple: [T, U]):[U, T]{
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
交換輸入的元祖
泛型約束
在函數(shù)內(nèi)部使用泛型變量的時(shí)候,由于事先不知道它是哪種類型,所以不能隨意操作它的屬性和方法;這時(shí)可以對(duì)泛型進(jìn)行約束。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
使用extends約束了泛型T必須符合接口Lengthwise,也就是必須包含length屬性。
泛型接口
//1.
interface ConfigFn{
<T>(value:T):T;
}
//2.
interface ConfigFn<T>{
(value:T):T;
}
const getData:ConfigFn = function<T>(value:T):T{
return value
}
getData<string>('hahaha')
泛型類
與泛型接口類似,泛型也可以用于類的類型定義中
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; };
泛型參數(shù)的默認(rèn)類型
//當(dāng)使用泛型時(shí)沒(méi)有在代碼中直接指定類型參數(shù)
//TS也無(wú)法推倒出來(lái)類型時(shí),這個(gè)默認(rèn)值就會(huì)起作用
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;
}
命名空間
命名空間和模塊的區(qū)別:
- 命名空間:內(nèi)部模塊,主要用于組織代碼,避免命名沖突
- 模塊:TS的外部模塊簡(jiǎn)稱,側(cè)重代碼的復(fù)用,一個(gè)模塊里可能會(huì)有多個(gè)命名空間。
namespace A {
interface Animal {
name:string;
eat(str:string):void
}
export class Dog implement Animal {
name:string;
constructor(name:string){
this.name = name
}
eat(){
console.log(this.name + '吃肉肉')
}
}
const d = new A.Dog('小黑')
d.eat()
裝飾器(Class Decorator)
能夠幫助我們來(lái)注釋或者是修改代碼的行為-這鐘做法我們通常稱為元編程。
裝飾器能夠很好的抽象代碼,它們最合適用來(lái)包裝可能會(huì)多處復(fù)用的邏輯
裝飾器工廠可以傳參
裝飾器工廠模式。我們將裝飾器本身封裝在另外一個(gè)函數(shù)中,這樣就能給裝飾器傳遞變量了,例如
@Cool('stuff')。而當(dāng)你不想給裝飾器傳參,把外層那個(gè)函數(shù)去掉就好了@Cool。
function logClass(params:string){ // 裝飾器工廠
return function(target:any){ //裝飾器
console.log(target)
console.log(params)
target.prototype.apiUrl = params
}
}
@logClass('hello')
class HttpClient{
constructor(){
}
getData(){
}
}
裝飾器的重載類(構(gòu)造函數(shù)、方法)
類裝飾器使得開(kāi)發(fā)者能夠攔截類的構(gòu)造方法
function logClass(target:any){
console.log(target)
return class extends target{
apiUrl:any = '我是修改后的數(shù)據(jù)'
getData(){
this.apiUrl + '-----'
console.log(this.apiUrl)
}
}
}
@logClass
class HttpClient{
public apiUrl: string | undfined;
constructor(){
this.apiUrl = '我是構(gòu)造函數(shù)的apiUrl'
}
getData(){
console.log(this.apiUrl)
}
}
屬性裝飾器
屬性裝飾器表達(dá)式會(huì)在運(yùn)行時(shí)當(dāng)作函數(shù)被調(diào)用,轉(zhuǎn)入下列2個(gè)參數(shù);
- 原型對(duì)象
- 當(dāng)前屬性的名稱
function logProperty(params:any){
return function(target:any, propname: any){//1.原型對(duì)象 2.當(dāng)前屬性的名稱
console.log(target)
console.log(propname)
target[propname] = params
}
}
function logClass(params:string){
return function(target:any){
console.log(target)
console.log(params)
target.prototype.apiUrl = params
}
}
@logClass(‘xxxx’)
class HttpClient{
@logProperty('http://hahaha')
public Url: any | undfined;
constructor(){
this.apiUrl = '我是構(gòu)造函數(shù)的apiUrl'
}
getData(){
console.log(this.Url)
}
}
// 屬性裝飾器就可以使得我們?cè)趯?duì)屬性賦值之前或在取屬性之后附加一些操作,就像中間件那樣
export class IceCreamComponent {
@Emoji()
flavor = 'vanilla'
}
function Emoji(target: Object, key: string | symbol) {
let value = target[key];
const getter = () => {
return value;
}
const setter = (next) => {
console.log('改變')
value = next
}
Object.defineProperty(target, key , {
get: getter,
set: setter,
enumerable: true,
configurable: true,
})
}
方法裝飾器
它會(huì)被應(yīng)用到方法的 屬性描述符上,可以用來(lái)監(jiān)視,修改或者替換方法定義。
方法裝飾器會(huì)在運(yùn)行時(shí)傳入下列3個(gè)參數(shù)
- 對(duì)于靜態(tài)成員來(lái)說(shuō)是類的構(gòu)造函數(shù),對(duì)于實(shí)例成員是類的原型對(duì)象。
- 成員的名字
- 成員的屬性描述對(duì)象(6個(gè))
function (params:any) {
return function(target:any, methodsName:string, desc:any){
//修改裝飾器方法,把裝飾器方法里面?zhèn)魅氲乃袇?shù)改為string類型
//1.保存當(dāng)前的方法
const oldMethod = desc.value;
desc.value = function(...args:any[]){
args = args.map((value)=>{
return String(value);
})
oMethod.apply(this, args);
}
}
}
class HttpClient {
public url:any|undefined
constructor(){
}
@logMethod
getDate(...args:any[]){
console.log(this.url)
}
}
export class IceCreamComponent {
toppings = [];
@Confirmable('Are you sure?')
@Confirmable('Are you super, super sure? There is no going back!')
addTopping(topping) {
this.toppings.push(topping);
}
}
// Method Decorator
function Confirmable(message: string) {
return function (target: Object, key: string | symbol, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function( ... args: any[]) {
const allow = confirm(message);
if (allow) {
const result = original.apply(this, args);
return result;
} else {
return null;
}
};
return descriptor;
};
}
方法參數(shù)裝飾器
可以使用參數(shù)裝飾器為類的原型增加一些元素?cái)?shù)據(jù),傳入下列3個(gè)參數(shù)
- 對(duì)于靜態(tài)成員來(lái)說(shuō)是類的構(gòu)造函數(shù),對(duì)于實(shí)例成員是類的原型對(duì)象
- 參數(shù)的名字
- 參數(shù)在函數(shù)參數(shù)列表中的索引
function logParams(parms:any){
return function(target:any, methodName:any,paramsIndex:any){
console.log(target)
}
}
class HttpClient{
public url:any|undefind;
constructor(){
}
getData(@logParams('uuid') uuid:any){
console.log(uuid)
}
}
const http = new HttpClient()
http.getData(123456)
裝飾器的執(zhí)行順序
- 如果同一個(gè)方法有多個(gè)裝飾器,會(huì)像剝洋蔥一樣,先從外到內(nèi)進(jìn)入,然后由內(nèi)向外執(zhí)行。復(fù)合函數(shù)的形式。
理解為包裹從同一類型裝飾器,從后往前
屬性>>方法>>方法參數(shù)>>類裝飾器
- 屬性裝飾器
- 方法裝飾器
- 方法參數(shù)裝飾器
- 類裝飾器
普通函數(shù)-高階函數(shù)的形式實(shí)現(xiàn)
function doSomething(name) {
console.log('Hello, ' + name);
}
function loggingDecorator(wrapped) {
return function() {
console.log('Starting');
const result = wrapped.call(this, ...arguments);
console.log('Finished');
return result;
}
}
const wrapped = loggingDecorator(doSomething);