TypeScript (基礎)

TypeScript 是 JavaScript 的類型的超集,它能夠編譯成純 JavaScript。編譯出來的 JavaScript 能夠運行在任何瀏覽器上。TypeScript 編譯工具能夠運行在任何服務器和任何系統上。TypeScript 是開源的。html

如下內容均出自於 TS入門教程node

以及 Ts 官網的一些內容,沒有基礎的小夥伴直接看打了⭐️的內容便可。jquery

嘗試

看了以後怎麼搭個環境寫一寫?git

mkdir demo
cd demo
touch 1.ts
複製代碼

打開vscode,打開控制檯,切換到問題 tabes6

歐了,開始嘗試 ts 吧github

⭐️基礎類型

└boolean

let isDone: boolean = false;
// 使用構造函數 Boolean 創造的對象不是布爾值
複製代碼

└null & undefined

是全部類型的子類型npm

void類型不能賦值給 number編程

let u: undefined = undefined;
let n: null = null;
let num: number = undefined;
let u: undefined;
let num: number = u;
複製代碼

└void 類型

通常表示函數沒有返回值。用在變量上沒有什麼卵用。json

function warnUser(): void {
    console.log("This is my warning message");
}
let a: void = undefined
let a: void = 'undefined' // 報錯,這是字符串
複製代碼

跟它類似的類型還有undefinednull 在不開啓嚴格空檢查的狀況下 --strictNullChecks,他們能夠賦值給全部已經定義過***其餘類型***的變量。 也就是說他們是全部類型的子類型數組

let a: undefined = undefined
let a: null = null
複製代碼

└數字 number

TypeScript裏的全部數字都是浮點數。 這些浮點數的類型是 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;
複製代碼

└字符串 string

單雙引'' "",模板字符的都被視爲字符串

let str:string = ''
複製代碼

⭐️數組類型

有多種聲明數組的方式

  • 最簡單的方法是使用類型 + []來表示數組:
const arr: number[] = [1,2,3]
const arr2: string[] = ['1','2']
複製代碼
  • 數組泛型定義方式
const arr2: Array<number> = [1,2,3,3]
const arr2: Array<string> = [1,2,3,3]
複製代碼
  • 接口表示數組
interface NumArr {
    [index: number]: number;
}
let numArr: NumArr = [1,2,3];
複製代碼
  • any 類型數組
let list:any[] = [1,"z",{}]
複製代碼
  • 元組類型聲明
// 表示一個肯定數組長度和類型的寫法
const arr:[string,number] = ['2',3]
複製代碼
  • 類數組

就是僞數組的定義

官方已給了各自的定義接口 Arguments, NodeList, HTMLCollection

function sum() {
    let args: IArguments = arguments;
}
複製代碼

枚舉 enum

js中沒有這類型,仿照強類型語言來的。值只能爲數字,不定義默認值得狀況爲從0開始。

enum Color {Red, Green, Blue}
let c: Color = Color.Green;
// c = 1

enum Number {one = 10, two}
let c: Number = Number.two;
// c = 11
複製代碼

⭐️any 類型(任意值)

指代全部的類型

let a: any = '123'
let a = 123; // 不聲明默認 any
複製代碼

never

表示永遠不存在的值,通常會用來寫拋出異常或推斷爲返回值爲never的函數。(好比return一個其餘的never類型)

function error(message: string): never {
    throw new Error(message);
}
error('a')
複製代碼

object 類型

非簡單類型 也就是除number,string,boolean,symbol,null或undefined以外的類型。

function create(o: object | null): void{
  console.log(o);
};
create({ prop: 0 }); // OK
create(null); // OK
create([]); // OK
create('a'); // error
複製代碼

⭐️interface 接口

在 TypeScript 中,咱們使用接口(Interfaces)來定義對象的類型。

對象的描述

接口通常首字母大寫。

賦值的時候,變量必須和接口保持一致

interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom',
    age: 25
};
複製代碼

└可選屬性

不想徹底匹配某個接口,經過?表示這個屬性是可選的

仍然不容許添加未定義的屬性

interface Person {
    name: string;
    age?: number;
}

let tom: Person = {
    name: 'Tom'
};
複製代碼

└任意屬性

讓接口容許添加任意的屬性值

[propName: string]: any;

interface Person {
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom: Person = {
    name: 'Tom',
    gender: 'male'
};
複製代碼

一旦定義了任意屬性, 那麼肯定屬性和?可選屬性都必須是任意屬性的子集

interface Person {
    name: string;
    age?: number;
    [propName: string]: string;
}

let p:Person = {
    name: 'zzc',
    age: 12, // error , 定義了 propName 必須將值設定爲 string 類型
    gender: 'male' ,
}
複製代碼

└只讀屬性 readonly

至關因而常量了,初次賦值後不能從新賦值 作爲變量使用的話用 const,若作爲屬性則使用readonly

interface demo {
  readonly a: string; // readonly定之後不能改值
  b: number
}
let obj: demo = {
  a: 'ss',
  b: 1
}
obj.a = 'aa' // error
obj.b = 2 // success
複製代碼

只讀的約束存在於第一次給對象賦值的時候,而不是第一次給只讀屬性賦值的時候

interface Person {
    readonly id: number;
}
const tom: Person = {} // error
tom.id = 1 // error,
複製代碼

會報兩次錯,第一個是由於指定了 id,但沒有給 id 賦值

第二個錯是給只讀屬性id賦值了

└ReadonlyArray

經過ReadonlyArray定義的數組,再也沒法改變了。

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = [1,2,3];
a[0] = 10 // success
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // 注意! 將readonly的值賦值給一個可變得數組也是不行的。
a = ro as Array<any> // 可是能夠用斷言重寫
複製代碼

⭐️函數類型

常見的函數聲明方式有: 函數聲明 & 函數表達式

用 ts 定義函數要考慮它的輸入和輸出

  • 函數聲明方式定義
function sum(a:number,b:number):number{
    return a+b
}
// 形參和實參數量要一致
sum(1) // error
sum(1,2) //3
sum(1,2,3) // error
複製代碼
  • 函數表達式定義
// 方式 1
let sum = function(a:number,b:number):number {
    return a + b;
}
// 方式二
let sum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};
複製代碼

方式一中只對等號右側的匿名函數定義了類型,左邊是ts經過類型推論定義出來的

方式二纔是給 sum 定義類型,**其中的 => 不是 es6的 => ** ,它用來表示函數的定義,左邊是輸入類型,須要用括號括起來,右邊是輸出類型。

由於和 es6箭頭函數可能形成混淆,最好用方式一;

└可選參數

經過?給函數定義可選參數

可選參數後面不容許再出現必須參數了

若是給參數添加了默認值,ts 會自動識別爲可選,且不受上一條規則的限制。

function sum(a:number,b?:number){}
function sum(a?:number,b:number){} // error
function sum(a:number = 1,b:number){} // 默認值,識別爲可選,且不報錯
複製代碼

└...rest

使用…rest獲取剩餘參數,使用數組類型去定義它

剩餘參數必須是函數的最後一個參數

function (a, ...items:any[]){}
function (...items:any[], a){} // error
複製代碼

└函數的重載

重載容許一個函數接受不一樣數量或類型的參數時,做出不一樣的處理。

能夠重複定義一個函數的類型

function say(somthing:string):string; function say(somthing:number):string; // 以上是函數定義 // 如下是函數實現 function say(somthing:string|number):string|number {
    return somthing
}
複製代碼

注意,TypeScript 會優先從最前面的函數定義開始匹配,因此多個函數定義若是有包含關係,須要優先把精確的定義寫在前面。

⭐️類型斷言

類型斷言(Type Assertion)能夠用來手動指定一個值的類型。

判定這個變量的類型是啥

類型斷言不是類型轉換

兩種寫法

<類型>值 or 值 as 類型

若是在 tsx 語法中使用,必須用 as

  • 例子

聯合類型能夠指定一個變量爲多種類型,此變量只能訪問類型們的共有方法。

但一些狀況下咱們必須使用某一類型的方法或屬性時,就能夠用斷言

function say(something:number|string):void{
    alert(something.length) // 聯合類型,報錯
}
// ==> 使用斷言, 在變量前加上 <類型>
function say(something:number|string):void{
    alert( (<string>something).length ) // success } 複製代碼

斷言成一個聯合類型中不存在的類型是不容許的

function say(something:number|string):void{
    alert(<boolean>something.length) // 聯合類型沒有 boolean ,error } 複製代碼

⭐️declare 聲明

第三方庫會暴露出一個變量,讓咱們在項目中直接使用。

可是 ts 編譯時不知道這是啥,編譯沒法經過。

此時咱們就要用 declare var 聲明語句來定義他的類型

// 好比 jquery
$('div') // ERROR: Cannot find name 'jQuery'.

// ==> 使用 declare var 第三方庫變量: (參數: string) => 返回類型
declare var $: (selector: string) => any;

$('#foo'); // success
複製代碼

declare var 並非真正的聲明一個變量,編譯完會刪除,僅僅是定義類型。

└聲明文件

一般咱們會把聲明語句放到一個單獨的文件(*.d.ts)中,這就是聲明文件

聲明文件必需以 .d.ts 爲後綴

假如仍然沒法解析,那麼能夠檢查下 tsconfig.json 中的 filesincludeexclude 配置,確保其包含了 jQuery.d.ts 文件。

// src/jQuery.d.ts

declare var jQuery: (selector: string) => any;
複製代碼

這只是非模塊化項目中使用的例子

└第三方聲明文件

固然,jQuery 的聲明文件不須要咱們定義了,社區已經幫咱們定義好了:jQuery in DefinitelyTyped

咱們能夠直接下載下來使用,可是更推薦的是使用 @types 統一管理第三方庫的聲明文件。

@types 的使用方式很簡單,直接用 npm 安裝對應的聲明模塊便可,以 jQuery 舉例:

npm i @types/jquery -D
複製代碼

能夠在這個頁面搜索你須要的聲明文件。

└自定義聲明文件

聲明文件有如下方法

  • 全局變量:經過 <script> 標籤引入第三方庫,注入全局變量
  • npm 包:經過 import foo from 'foo' 導入,符合 ES6 模塊規範
  • UMD 庫:既能夠經過 <script> 標籤引入,又能夠經過 import 導入
  • 模塊插件:經過 import 導入後,能夠改變另外一個模塊的結構
  • 直接擴展全局變量:經過 <script> 標籤引入後,改變一個全局變量的結構。好比爲 String.prototype 新增了一個方法
  • 經過導入擴展全局變量:經過 import 導入後,能夠改變一個全局變量的結構

這裏只記錄 npm 導入的方法, 其餘請看 書寫聲明文件

在給一個第三方庫寫聲明文件以前,先查看這個庫有沒有聲明文件。通常來講,npm 包的聲明文件可能存在於兩個地方:

  1. 跟 npm 包綁在一塊兒, package.json 中有type 字段,或者有 index.d.ts 聲明文件。通常經常使用的包都有了,本身要發佈 npm 包的時候最好也綁定在一塊兒。
  2. 發佈到了@types](microsoft.github.io/TypeSearch/) 裏,通常這種狀況是做者沒寫,其餘人寫了發上去的。npm install @types/foo --save-dev 直接經過安裝。

若是都沒有,才本身寫。

聲明文件存放的位置是有約束的,通常在兩個位置。

  1. node_modules 建立第三方庫的聲明文件,但這種通常不採納。通常 node_modules 不會隨咱們的應用發佈到服務器|git上。
  2. 建立一個types 目錄來寫,要配合tsconfig.json 來使用。
# 項目結構
├── README.md
├── src
|  └── index.ts
├── types
|  └── foo
|     └── index.d.ts
└── tsconfig.json
複製代碼
  • 配置
{
    "compilerOptions": {
        "module": "commonjs",
        "baseUrl": "./",
        "paths": {
            "*" : ["types/*"]
        }
    }
}
複製代碼

無論採用了以上兩種方式中的哪種,我都強烈建議你們將書寫好的聲明文件(經過給原做者發 pr,或者直接提交到 @types 裏)發佈到開源社區中,享受了這麼多社區的優秀的資源,就應該在力所能及的時候給出一些回饋。只有全部人都參與進來,才能讓 ts 社區更加繁榮。

  • export

npm 包寫的聲明文件 declare 不會聲明一個全局變量,只有導出的時候纔會應用類型聲明。

export const name: string; // 簡單類型
export function getName(): string; // 函數 export class Animal { // class 聲明
    constructor(name: string);
    sayHi(): string;
}

export interface Options { // 接口
    data: any;
}

// ===> 對應使用到項目中
import { name, getName, Animal, Directions, Options } from 'foo';
let myName = getName();
let cat = new Animal('Tom');
let options: Options = {
    data: {
        name: 'foo'
    }
}
複製代碼
  • 混用 declare export

經過 declare 定義多個變量,一次性導出

declare const name: string;
declare function getName(): string; declare class Animal {
    constructor(name: string);
    sayHi(): string;
}

export {
	name,
    getName,
    Animal,
}
複製代碼
  • 導出默認值

只有 functionclassinterface 能夠直接默認導出,其餘的變量須要先定義出來,再默認導出

針對默認導出,通常會把導出語句煩惱歌在聲明文件的最前面。

export default function foo(): string; export default interface Options {
    data: any
}
export default class Person {
    constructor(name: string);
    sayHi(): string;
}
declare const str:string;
export default str;
複製代碼
  • export namespace

namespace 原本是 TS 的模塊化方案,隨着 es6愈來愈屌基本已經不在 ts 中使用了。

可是聲明文件中仍是很經常使用的,表示變量是一個包含了子屬性的對象類型。

像是lodash ,它是個對象,但提供了不少子屬性方法如 lodash.debunce

若是對象擁有深層的層級,則須要用嵌套的 namespace 來聲明深層的屬性的類型:

總的來講,用來導出一個擁有子屬性的對象。

export namespace obj {
    const name: string;
    function fn(a:string,b?:nnumber):void; class Event {
        say(str:string):void
    };
    // 若是還有包含子屬性的對象,就嵌套
    namespace sonObj {
        const foo: string;
    }
}
複製代碼

└聲明合併

當一個變量,既是函數又是對象。能夠組合多個語句聲明。

export function objAndFn(foo:string):any; export namespace objAndFn {
    function fn(boo:number):void; const name:string; } 複製代碼

└針對 commonjs 規範

用如下方式來導出:

// 總體導出
module.exports = foo;
// 單個導出
exports.bar = bar;
複製代碼

在 ts 中,針對這種導出,有多種方式能夠導入,第一種方式是 const ... = require

// 總體導入
const foo = require('foo');
// 單個導入
const bar = require('foo').bar;
複製代碼

第二種方式是 import ... from,注意針對總體導出,須要使用 import * as 來導入:

// 總體導入
import * as foo from 'foo';
// 單個導入
import { bar } from 'foo';
複製代碼

第三種方式是 import ... require,這也是 ts 官方推薦的方式:

// 總體導入
import foo = require('foo');
// 單個導入
import bar = require('foo').bar;
複製代碼

對於 commonjs 規範的庫,須要使用 export = 變量 的語法寫聲明文件

準確地講,export = 不只能夠用在聲明文件中,也能夠用在普通的 ts 文件中。實際上,import ... requireexport = 都是 ts 爲了兼容 AMD 規範和 commonjs 規範而創立的新語法

export = foo;
declare function foo():string; declare namespace foo {
    const bar: nnumber;
}
複製代碼

⭐️內置對象

TS定義了 js 中內置對象的類型,在 TypeScript 核心庫的定義文件中。

包括 ECMAscript、DOM、Bom 等

這些內置對象的類型在.ts 中均可以直接使用

let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
  // Do something
});
複製代碼

還會在該內置對象中定位錯誤

Math.pow(10, '2'); // error 必須是兩個 number 型
document.addEventListener('click', function(e) {
    console.log(e.targetCurrent);
    // error, MouseEvent 類型不存在 targetCurrent屬性
});

複製代碼

內置對象不包含 Node ,若是要使用

npm install @types/node --save-dev
複製代碼

這章是介紹面向對象編程

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
        console.log(this.greeting); // world
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}
let greeter = new Greeter("world");
複製代碼

繼承

基本繼承 做爲基類的類通常被叫作超類,繼承的類叫作派生類

class Car {
  name: string = '大衆';
  by() {
    console.log(this.name);
  }
}
class Baoma extends Car {
  name: string = '寶馬'
}
let bm = new Baoma()
bm.by()
複製代碼

在子類constructor構造函數中***必須調用一下super***,它會執行基類的構造函數 在子類constructor構造函數調用this以前必須先調用super

class FClass {
  name: string = '超類'
  constructor(name: string) { this.name = name; }
  setAge(age:number) {
    console.log(`${this.name} is ${age}`);
  }
}
class SClass extends FClass {
  constructor(name:string) {
    super(name)
  }
}
let f = new SClass('zzc')
f.setAge(20)
複製代碼

公共,私有與受保護的修飾符

public能夠被除本身以外的因此子類訪問到 ts中全部成員默認爲public 當一個成員被標記成private 時,沒法被外部訪問 若是類的constructor聲明成了private那就沒辦法new 和繼承了。 protected 受保護的,跟private基本同樣,但它在子類中能夠訪問;

class FClass {
  name: string = '超類'
  private constructor(name: string) { this.name = name; }
  setAge(age:number) {
    console.log(`${this.name} is ${age}`);
  }
}
let f = new FClass() // error
class Son extends FClass {} // error
複製代碼

protected 受保護的例子

class F {
  protected name: string;
  constructor(name: string) { this.name = name; }
  setAge(age:number) {
    console.log(`${this.name} is ${age}`);
  }
}
class Son extends F {
  constructor() {
    super('son')
  }
  getName() {
    super.name // 能夠在子類訪問
  }
}

let f = new F('super class')
let s = new Son()

console.log(s.name); // 不能在外部訪問
console.log(s.getName()); // 但能夠經過子類的方法return出來獲取到
複製代碼

在類中使用readonly

在類中或者constructor都還能夠更改readonly的值,但在外部就沒法更改了。

class F {
  readonly a:number = 8
  constructor(age:number) {
    this.a = age
  }
}
let f = new F(9)
f.a = 10 // error 沒法在外部更改

複製代碼
  • 參數屬性

constructor用一句話定義並賦值一個屬性 只要在參數前面加了訪問限定符就能夠直接給一個屬性直接賦值 readonly protected public private static 是私有的,因此不能加。

class F {
  readonly a:number = 8
  constructor(readonly b:number) {
    b = 10
  }
}
let f = new F(9)

console.log(f); // {a,b}
複製代碼

存取器 getter/setters

當一個屬性只有get方法的時候,它就是隻讀的。 這也是一種外部改變靜態屬性的方法

// 當a = 'a' 時,內部的_a纔會等於賦的值,不然報錯。
class F {
  private _a:string;
  get a():string {
    return this._a
  }
  set a(newA:string) {
    if(newA === 'a') {
      this._a = newA
    }
    else {
      this._a = 'error'
    }
  }
}
let f = new F()

f.a = 'b'
console.log(f);
複製代碼

靜態屬性 static

它只掛在class自己,而不是經過new實例化後出來的對象 因此你能夠經過 類.static屬性 來調用,但不能用this

class F {
    static num: number;
    changeStatic() {
      F.num = 19;
    }
    constructor () {
      this.changeStatic()
      console.log(F.num);
    }
}
let f = new F();
複製代碼

抽象類 & abstract

abstract 用來定義抽象類 和 在抽象類中定義抽象方法的 抽象類就是派生類的一個模板類,通常不會把它實例化,只是給子類繼承用的。

abstract class Animal { // 抽象一個Animal類
    abstract makeSound(): void;  // 抽象一個方法,必須在子類實現它
    move(): void {
        console.log('roaming the earch...');
    }
    constructor () {

    }
}
class Son extends Animal {
  constructor() {
    super()
  }
  makeSound() { // 必須實現抽象類中的方法
    return false
  }
  haha() {
    console.log('error');
    
  }
}

let s:Animal // 能夠指定抽象類爲一個類型
s.haha() // 若是上面的聲明瞭,那麼調用抽象類中不存在的haha方法是不容許

s = new Animal() // 不能夠new 抽象類
s = new Son()  // 正確
s.makeSound() // 正確
複製代碼

⭐類型推論

若是沒有明確的指定類型,那麼 TypeScript 會依照類型推論(Type Inference)的規則推斷出一個類型。

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7; // error

// 等價於 ==>
let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;
複製代碼

若是定義的時候沒有賦值,無論以後有沒有賦值,都會被推斷成 any 類型而徹底不被類型檢查

let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
複製代碼

⭐️聯合類型

表示變量能夠是多種類型其中的一種

經過 | 分隔

let numOrStr : string | number;
numOrStr = 7;
numOrStr = '7';
numOrStr = true; // false
複製代碼

當調用聯合類型的方法時,只能調用倆類型中共有的方法。

let numOrStr : string | number;
numOrStr.length // 報錯 length 不是 number 的方法
numOrStr.toString() // 能夠
複製代碼
相關文章
相關標籤/搜索