本片文章主要講述了TypeScript的基礎知識點,這些是我個人的理解,如有不正確的地方請評論斧正!
文章以下面的順序講解:
- 變數型別
- 函式
- 類
- 介面
- 泛型
- 名稱空間
- 聯合型別
- 型別斷言
在開始之前我們先裝環境:
npm i typescript -g //全域性安裝typescript
npm init -y //進入資料夾,初始化專案,生成package.json檔案
tsc --init //建立tsconfig.json檔案
npm i @types/node -S //這個主要是解決模組的宣告檔案問題
複製程式碼
環境到此安裝結束;
一,變數型別
1.number 型別
let num1 : number = 20;
let num2 : number = 175.5;
let a1 : number = Infinity; //正無窮大
let a2 : number = -Infinity; //負無窮小
let a3 : number = NaN;
複製程式碼
注意:Infinity, -Infinity, NaN 也屬於Number型別
2.undefined 型別
let un : undefined = undefined;
複製程式碼
注意:
undefined 型別的資料只能被賦值為 undefined
在 typescript中,已宣告未初始化的值要直接訪問的話,型別需要定義為undefined
3.null 型別
let nu : null = null;
複製程式碼
注意:
null 型別只能被被賦值為null
null是一個空指標物件,undefined是未初始化的變數,所以,可以把undefined看成一個空變數,把unll看成一個空物件。
特別注意: 預設情況下,undefined 和 null 型別,是所有其它型別的子型別,也可以說成,它倆可以給所有其他型別賦值。
4.string 型別
//值型別
let str : string = '你好!'
//引用型別
let str1 : String = new String('你好!')
複製程式碼
5. boolean 型別
let boo : boolean = true;
let boo1 : boolean = false
複製程式碼
6.symbol 型別
let sy : symbol = Symbol('bar');
複製程式碼
注意: symbol型別的值是通過Symbol建構函式建立的。
7. 陣列型別
//字面量
let arr1 : number[] = [1, 2]
//泛型---->相當於陣列中每個元素的型別
let arr2 : Array<string> = ['a', 's']
//建構函式
let arr3 : string[] = new Array('a', 's')
//聯合型別-->這裡的聯合型別的意思是,陣列中元素的型別可以是number 或 string,兩種都有也可以
let arr4 : Array<number | string> = [2, 'a']
複製程式碼
8.元組型別(tuple)
let tup : [string,number] = ['asdasd', 43233];
複製程式碼
注意:
元組和陣列看起來有點類似,但是,是有區別的
元組的長度是有限的,而且分別為每一個元素定義了型別
9. 列舉型別(enum)
enum--->組織收集一組相關變數的方式。
數字列舉
enum REN {
// nan = 1 ----->初始化下標
nan,
nv,
yao
}
console.log(REN.nan)//0
console.log(REN.nv)//1
console.log(REN.yao)//2
//使用數字列舉時,TS 會為列舉成員生成反向對映
console.log(REN[2])// yao
複製程式碼
注意:
數字的列舉---->下標從0開始,也可以自行設定列舉成員的初始值,它們會依次遞增
字串列舉
enum SIJI {
chun = '春',
xia = '夏',
qiu = '秋',
dong = '冬'
}
console.log(SIJI.chun)//春
console.log(SIJI.xia)//夏
console.log(SIJI.qiu)//秋
console.log(SIJI.dong)//冬
複製程式碼
注意:
字串列舉型別允許使用字串來初始化列舉成員,可以是一個字串字面量或者另一個字串的列舉成員
字串列舉型別不支援成員自增長,每個成員必須初始化,另外字串列舉不會為成員生成發向對映
10. void 型別
void 型別--->表示沒有任何返回值,一般用於定義方法時方法沒有返回值
function f1() : void {
console.log('void型別')
}
複製程式碼
注意:
這裡你也可以指定返回值型別為 undefined。因為 JS 中,如果函式沒有返回值,則會預設返回 undefind。不過,使用 void 型別可以使表意更清晰。
11. any 型別
注意:
其他型別都是any型別的子型別 ,any型別的值可以被賦值為任何型別的值
let an : any = 'any 型別';
console.log(an)//any 型別
an = 25;
console.log(an)//25
複製程式碼
注意:對於any 需要注意兩點
如果在宣告變數時,沒有宣告其型別,也沒有初始化,(因為型別推斷會自動判斷型別),那麼它就會被判斷為any型別
let an1;
an1 = '沒有宣告其型別,也沒有初始化';
console.log(an1)//沒有宣告其型別,也沒有初始化
an1 = 25
console.log(an1)//25
複製程式碼
在any型別變數上可以訪問任何屬性,即使它不存在
let something: any = 42
something.mayExist() // 沒問題,因為其可能在執行時存在
something.toFixed() // 沒問題,雖然確實存在,但是編譯器並不會去檢查
複製程式碼
12. never 型別
注意:
never 表示永遠不會存在的值的型別, never 是任何型別的子型別,但是 沒有任何型別是never的子型別或可以賦值給never型別(除了never本身之外)。 即使 any也不可以賦值給never。
never 型別常用於兩種情況
用於描述從不會有返回值的函式---》返回never的函式必須存在無法達到的終點
function f5() : never {
while (true) {
// do something
}
}
複製程式碼
用於描述總丟擲錯誤的函式
function f2(msg : string) : never {
throw new Error(msg)
}
複製程式碼
13. 日期型別
let da : Date = new Date()
console.log(da)
複製程式碼
14. 正規表示式型別
//建構函式宣告法
let reg1 : RegExp = new RegExp('ljy','gi')
console.log(reg1)
//字面量的宣告法
let reg2 : RegExp = /ljy/gi
console.log(reg2)
複製程式碼
二, 函式
1. 函式定義
定義函式有函式宣告和函式表示式兩種形式。定義函式的引數和返回值可以指定其型別;當呼叫函式時,傳入引數型別必須與定義函式引數型別保持一致。
函式宣告定義
// 引數型別 返回值型別
function f(age:number) : string {
return `找到了${age}的小哥哥`;
}
let age : number = 22;
let res : string = f(age);
console.log(res)
複製程式碼
函式表示式定義
let f1 = (age:number) : string => {
return `找到了${age}的小哥哥`;
}
let age1 :number = 21;
let res1 : string = f1(age1);
console.log(res1)
複製程式碼
注意:表示式定義完以後,必須呼叫函式
函式表示式還有一種寫法:
函式表示式:指定變數fn的型別
注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>
在 TypeScript 的型別定義中,=> 用來表示函式的定義,左邊是輸入型別,需要用括號括起來,右邊是輸出型別。
// let fn: (x: Type, y: Type) => Type = (x, y) => {}
//例子
var run3: (x: number, y: number) => string = function(x: number, y: number): string{
return 'run3';
}
console.log(run3(1, 2))
//當給變數run3指定型別的時候,應該是函式的引數和返回值的約束型別。如果用後面學到的ts型別推論,可以簡寫為:
var run4: (x: number, y: number) => string = function(x, y){
// 型別推論可以確定函式的引數和返回值型別,也就可以省略型別指定
return 'run4';
}
console.log(run4(1, 2))
複製程式碼
2. 函式沒有返回值可以使用void型別值定返回值
function f3() : void {
console.log('沒有返回值')
}
f3()
複製程式碼
3. 可選引數的函式
注意:可選引數一定要放在引數的最後面
function f4(age:number, cm?:number) : string {
//cm為可選引數,可傳可不傳
if (cm) {
return `可選引數------身高為${cm}釐米`;
} else {
return `可選引數-----年齡${age}歲`
}
}
console.log(f4(12))
console.log(f4(24, 175))
複製程式碼
4. 有預設值引數的函式
注意:ts會將新增了預設值的引數識別為可選引數,有預設值的引數的位置不受【可選引數必須放在後面】的限制
function f5(age:number, cm:number = 188) : string {
return `預設引數----年齡為${age}歲---身高為${cm}cm`
}
console.log(f5(25))
複製程式碼
5. 剩餘引數的函式
//當有很多引數的時候,或者引數個數不確定,可以用三點運算子
function f6(...rest:number[]) : number[] {
return [...rest];
}
console.log(f6(1,2,3,4,5,6,7,8,9))
function f7(a:number, b:number, ...rest:number[]) : number[] {
return [a, b, ...rest]
}
console.log(f7(100,200,1,2,3,4,5,6))
複製程式碼
6. 介面中的函式
第一種寫法
interface int1 {
say (age:number) : void //抽象方法
}
複製程式碼
第二種寫法
interface int2 {
say : (age:number) => void //抽象方法
}
複製程式碼
7.函式的過載
注意:
先宣告所有方法過載的定義,不包含方法的實現
再宣告一個引數為any型別的過載方法
實現any型別的方法並通過引數型別(和返回型別)不同來實現過載
typescript中的過載:通過為同一個函式提供多個函式型別定義來實現多種功能的目的
TypeScript 會優先從最前面的函式定義開始匹配,所以多個函式定義如果有包含關係,需要優先把精確的定義寫在前面。
function f1(x: number, y: number): number;
function f1(x: string, y: string): string;
// 上面定義函式的格式,下面定義函式的具體實現
function f1(x: any, y: any): any {
return x + y;
}
f1(1, 2);
f1('a', 'b');
複製程式碼
三, 類
1. 訪問修飾符
public:公共修飾符
注意:
表示屬性或方法都是公有的,在類的內部,子類的內部,類的例項都能被訪問,預設情況下,為public
class People {
public name : string
constructor (name:string) { //建構函式必須寫
this.name = name
}
public say () :void {
console.log('你好')
}
}
複製程式碼
private 私有修飾符
注意:
表示在當前類中可以訪問,子類,外部類不可以訪問
class People {
private name : string
constructor (name:string) { //建構函式必須寫
this.name = name
}
private say () :void {
console.log('你好')
}
}
複製程式碼
protected 保護型別
注意:
表示在當前類中和子類中可以訪問,外部類不可以訪問
class People {
protected name : string
constructor (name:string) { //建構函式必須寫
this.name = name
}
protected say () :void {
console.log('你好')
}
}
複製程式碼
注意:
TypeScript 只做編譯時檢查,當你試圖在類外部訪問被 private 或者 protected 修飾的屬性或方法時,TS 會報錯,但是它並不能阻止你訪問這些屬性或方法。
readonly 只讀修飾符
注意:
表示某個屬性是隻讀的,不能被修改
class People {
readonly name : string
constructor (name:string) { //建構函式必須寫
this.name = name
}
}
複製程式碼
2. 宣告類
class People {
name : string //預設為public
age : number
constructor (name:string, age:number) { //建構函式必須寫
this.name = name
this.age = age
}
say () :void {
console.log('你好')
}
}
const HH : People = new People('含含', 21)
console.log(HH.name)
console.log(HH.age)
HH.say()
複製程式碼
3. 類的繼承
class Student extends People {
cm : number
constructor (name:string, age:number, cm:number) {
super(name, age) //super 繼承父類的建構函式,並向建構函式傳參,super必須寫在第一行
this.cm = cm
}
work () : void {
console.log('學習')
}
}
const stu1 : Student = new Student('liu', 22, 175)
console.log(stu1.name)
console.log(stu1.age)
console.log(stu1.cm)
stu1.say()
stu1.work()
複製程式碼
4. 靜態屬性和靜態方法
注意:
靜態方法和靜態屬性必須使用類名呼叫
靜態屬性和靜態方法在例項化之前就已經存在
class People {
static name1 : string = '靜態屬性';
static say () :void {
console.log('靜態方法')
}
}
console.log(People.name1)
People.say()
複製程式碼
注意:靜態方法呼叫不了例項化方法和例項化屬性,因為靜態域載入是在解析階段,而例項化是在初始化階段,(java原理),所以靜態方法裡面不能呼叫本類的方法和屬性,可以呼叫靜態屬性和靜態方法
5. 多型
多型---->重寫方法
父類定義一個方法不去實現,讓繼承它的子類去實現,每個子類的該方法有不同的表現
class Animal {
name : string
constructor (name:string) {
this.name = name
}
eat () : void {
//讓它的子類去實現不同的eat方法
}
}
class Laohu extends Animal {
constructor (name:string) {
super(name)
}
eat () : void {
console.log(`${this.name}吃肉!`)
}
}
class Laoshu extends Animal {
constructor (name:string) {
super(name)
}
eat () : void {
console.log(`${this.name}吃糧食!`)
}
}
const laohu : Laohu = new Laohu('老虎')
laohu.eat()
const laoshu : Laoshu = new Laoshu('老鼠')
laoshu.eat()
複製程式碼
6. 類和介面
注意:
類可以實現(implement)介面。通過介面,你可以強制地指明類遵守某個契約。你可以在介面中宣告一個方法,然後要求類去具體實現它。
介面不可以被例項化,實現介面必須重寫介面中的抽象方法
interface Play {
plays (difang:string) : void;
}
class Playy implements Play {
plays(difang: string): void {
console.log(`我們要去${difang}玩!!!`)
}
}
const pl : Playy = new Playy();
pl.plays('北京')
複製程式碼
注意:類和介面的區別
類可以實現(implement)多個介面,但只能擴充套件(extends)自一個抽象類。
抽象類中可以包含具體實現,介面不能。
抽象類在執行時是可見的,可以通過 instanceof判斷。介面則只在編譯時起作用。
介面只能描述類的公共(public)部分,不會檢查私有成員,而抽象類沒有這樣的限制。
7. 抽象類和抽象方法
注意:
用abstract關鍵字定義抽象類和抽象方法,抽象類中的抽象方法不包含具體實現並且必須在派生類(抽象類的子類)中實現
抽象類:它是提供其他類繼承的基類,不能直接被例項化,子類繼承可以被例項化
abstract修飾的方法(抽象方法)只能放在抽象類裡面
抽象類和抽象方法用來定義標準(比如定義標準為:抽象類Animal有抽象方法eat,要求它的子類必須包含eat方法)
abstract class People {
name : string
constructor (name:string) {
this.name = name
}
abstract eat (food:string) :void;//抽象方法不包括具體實現,並且必須再派生類中實現
}
class Stud1 extends People {
//抽象類的子類必須實現抽象類中的抽象方法
constructor (name:string) {
super(name)
}
eat(food: string): void {
console.log(`我愛吃${food}`)
}
}
const stu11 : Stud1 = new Stud1('liu')
stu11.eat('麵條')
複製程式碼
四,介面
注意:
介面定義:介面是對傳入引數進行約束;或者對類裡面的屬性和方法進行宣告和約束,實現這個介面的類必須實現該介面裡面屬性和方法;typescript中的介面用interface關鍵字定義。
介面作用:介面定義了某一批類所需要遵守的規範,介面不關心這些類的內部狀態資料,也不關心這些類裡方法的實現細節,它只規定這批類裡必須提供某些方法,提供這些方法的類就可以滿足實際需要。typescrip中的介面類似於java,同時還增加了更靈活的介面型別,包括屬性、函式、可索引和類等。
1. 屬性介面
對傳入物件的約束,也就是json資料
interface Sx {
name : string
age : number
}
function f8(peop:Sx) {
//name age 必須傳遞
console.log(peop)
}
const obj = {
name : 'liu',
age : 25
}
f8(obj)
複製程式碼
2. 函式型別的介面
對方法傳入的引數和返回值進行約束
interface Sta {
(difang : string, todo : string) : string
}
let play : Sta = (difang:string, todo:string) : string => {
return `我們去${difang}吃${todo}`
}
console.log(play('灞橋', '吃燒烤'))
複製程式碼
3. 可索引的介面
對索引和傳入的引數的約束
//對陣列的約束
interface UserArr {
//索引為number,引數為string
[index : number] : string
}
const arr : UserArr = ['a', 'b']
console.log(arr)
//對 物件的約束
interface UserObj {
[index : number] : number
}
const obj1 : UserObj = { 2:1, 3:4 }
console.dir(obj1)
複製程式碼
4. 類 型別介面
對類的約束
interface Anmal {
//對類裡面的屬性和方法進行約束
name : string
eat (food:string) : void
}
//類實現介面要用implements , 子類必須實現介面裡面宣告的屬性和方法
class Laoshu implements Anmal{
name : string
constructor (name : string) {
this.name = name
}
eat(food:string):void {
console.log(`${this.name}吃${food}`)
}
}
const lao : Laoshu = new Laoshu('老鼠')
lao.eat('糧食')
複製程式碼
5. 介面繼承
//父類Anmal看上面
//實現LaoHu的這個介面,必須也要實現LaoHu繼承的Anmal介面中的方法
interface LaoHu extends Anmal{
say (sa : string) : void
}
//繼承並實現介面
class XiaoLaoHu implements LaoHu{
name : string
constructor (name : string) {
this.name = name
}
eat (food : string) : void {
console.log(`${this.name}吃${food}`)
}
say(sa: string): void {
console.log(`${this.name}說${sa}`)
}
}
const xiao : XiaoLaoHu = new XiaoLaoHu('老虎')
xiao.eat('肉')
xiao.say('你好')
複製程式碼
五, 泛型
注意:
很多時候,型別是寫死的,不利於複用,泛型可以簡單的理解為給型別的這種值設定變數,解決類,介面
方法的複用性,以及對不特定資料型別的支援
語法 : <型別變數名> 一般是單字母大寫
1. 泛型函式
函式再呼叫時,指定泛型T的型別
function f9<T>(value:T) : T {
//傳入引數型別為T,返回值的型別也為T
console.log(`我傳入了${value}`)
return value
}
f9<number>(10)
function f10 <T> (value:T) : any {
//傳入引數的型別為T,返回任意型別的值
console.log(`我返回了${value}`)
return `我返回了${value}`
}
console.log(f10<string>('我是ljy'))
複製程式碼
2. 泛型類
泛型類,使用 < > 跟在類名後面
class Ni <T> {
name : T
constructor (name : T) {
this.name = name
}
say (value : T) : any {
return `${this.name}說${value}`
}
}
const ni1 = new Ni<string>('ljy')//例項化類,指定類的型別是string
console.log(ni1.say('你好'))
const ni2 = new Ni<number>(20)//例項化類,指定類的型別是number
console.log(ni2.say(23))
複製程式碼
3. 泛型介面
第一種
interface Niniubi {
<T> (value:T) : any
}
let fff : Niniubi = <T>(value : T) : any => {
return `我傳入了${value}`
}
console.log(fff<number>(25))
console.log(fff<string>('ljy'))
複製程式碼
第二種
interface ConfigFnTwo<T>{
(value:T):T;
}
function setDataTwo<T>(value:T):T{
return value
}
var setDataTwoFn:ConfigFnTwo<string> = setDataTwo
setDataTwoFn('name');
複製程式碼
4.在泛型約束中使用型別引數
有時,我們想讓引數的型別是某個物件的屬性時,可以這樣寫:
function getProperty<T, k extends keyof T>(obj:T, key: K) {
return obj[key]
}
let obj = { a: 1, b: 2, c: 3}
getProperty(obj, "a"); // true
getProperty(obj, "t"); // error: Argument of type '"t"' is not assignable to
parameter of type '"a" | "b" | "c"'.
複製程式碼
錯誤的原因:首先keyof T就等於"a" | "b" | "c",然後K extends就相當於K在keyof T中,這就是約束。
六,名稱空間
namespace Shuaige {
export class DeHua {
public name : string = '劉德華'
say () {
console.log(`我是${this.name}`)
}
}
}
namespace Bajie {
export class DeHua {
public name : string = '馬德華'
say () {
console.log(`我是${this.name}`)
}
}
}
const de : Shuaige.DeHua = new Shuaige.DeHua()
de.say()
const de1 : Bajie.DeHua = new Bajie.DeHua()
de1.say()
複製程式碼
七, 聯合型別
聯合型別表示一個值可以是幾種型別之一,我們使用( | )分隔每個型別
聯合型別的變數在被賦值的時候,會根據型別推論的規則推斷出一個型別
如果一個值是聯合型別,我們只能訪問此聯合型別的所有型別裡面共有的成員
let ddd : string | number
ddd = 'nihao'
console.log(ddd.length)//ddd被推斷成了 string,訪問它的 length 屬性不會報錯
console.log(`聯合型別${ddd}`)
ddd = 255
console.log(`聯合型別${ddd}`)
console.log(ddd.length)//報錯 ddd被推斷成了 number,訪問它的 length 屬性時就報錯了
//ddd = false err
//console.log(`聯合型別${ddd}`) err
複製程式碼
1. 訪問聯合型別的屬性或方法
當 TypeScript 不確定一個聯合型別的變數到底是哪個型別的時候,我們只能訪問此聯合型別的所有型別裡共有的屬性或方法:
function f11(name : string, age : string | number) {
console.log(age.length)//報錯
}
f11('ljy', '21')
報錯:Property 'length' does not exist on type 'string | number'.Property 'length' does not exist on type 'number'.
複製程式碼
上例中,length 不是 string 和 number 的共有屬性,所以會報錯。所以只能訪問型別的共有的屬性或方法
function f12(name : string, age : string | number) {
console.log(age.toString)
}
f12('ljy', 21)
複製程式碼
八, 型別斷言
注意:型別斷言(Type Assertion)可以用來手動指定一個值的型別。
語法:
<型別>值
或
值 as 型別
複製程式碼
型別斷言的用法如上,在需要斷言的變數前加上 即可
就剛才上邊TypeScript 不確定一個聯合型別的變數到底是哪個型別的時候來說
function f13(name : string, age : string | number) {
if (age.length) { //報錯
console.log(age.length) //報錯
} else {
console.log(age.toString)
}
}
f13('ljy', 21)//Property 'length' does not exist on type 'string |number'.Property 'length' does not exist on type 'number'
複製程式碼
此時可以使用型別斷言,將 age 斷言成 string
function f14(name : string, age : string | number) {
if ((<string>age).length) {//斷言
console.log((<string>age).length)//斷言
} else {
console.log(age.toString)
}
}
f14('ljy', 21)
複製程式碼
型別斷言不是型別轉換,斷言成一個聯合型別中不存在的型別是不允許的:
function toBoolean(something: string | number): boolean {
return <boolean>something;
}
Type 'string | number' cannot be converted to type 'boolean'
複製程式碼
本人經過閱讀,ts官方文件,jspang的ts教程,Hopsken的ts教程,Staticy的ts教程,才入門的,再此感謝!