因爲在春節期間,實在沒什麼事幹,就想系統的寫份typescript
筆記。廢話很少說,爲何咱們都在說着ts
這件事呢,尤爲對於前端開發者來講,JavaScript
做爲行爲交互,是四劍客之一。那麼爲何還要用ts
呢?javascript
咱們知道,目前國內主流的3大前端開發框架:html
框架名稱 | version | github star |
---|---|---|
React | V16.12.0 | 142,715 |
Vue.js | V2.x | 155,969 |
Angular | 56,653 |
tips:這裏暫不說跨平臺解決方案,好比比較優秀的Flutter
等。 固然了咱們會有一個疑問,假若咱們用ts
編碼,會不會現有的框架會不支持,這點我們大可沒必要擔憂。前端
那咱們想再看下,當前比較流行的框架、庫,它們的源碼狀況vue
正是因爲java
ts
的比重愈來愈多ts
重構ts
以及咱們衆所周知的JavaScript
自己存在的一些不夠完美的地方,例如xxx 未定義
,等等。固然了在一些原有的項目中可能也不習慣從新接觸ts
,那麼我和你們一塊來來作作筆記,這對於咱們仍是頗有必要的。node
編輯器 這裏推薦很好用的Vscode
,本筆記相關代碼也是用此react
相關環境版本git
第三方輔助github
npm install -g ts-node
複製代碼
具體使用參考 ts-node xxx.tstypescript
npm i -g nodemon
複製代碼
看過我其餘筆記的掘友,知道我是剛搞了臺顯示器
,那電腦仍是用的比較陳舊的不靈敏的mac
,因此可能會少些xmind 圖
什麼的,但願您也能看下去,這麼無聊的文字。筆記主要分爲三部分
首先,咱們不論是讀什麼文章,或者茶餘飯後同事嘮嗑,多少都有耳聞,ts
相比於js
會有幾點不一樣
對這幾點對於編程來講仍是尤其重要的,咱們先看幾個簡單的demo
一些簡單的瀏覽器調試,我們選擇在vscode
裏直接預覽,這裏推薦一個插件
具體的使用方法可參考文末
aString
是一個字符串,可又能從新賦值爲數字
let aString = `我是一個字符串` aString = 123 // alert(aString) console.log(aString) const addFn = (num1, num2) => alert(num1 + num2) console.log(1, 2) console.log('1', '2') console.log('1', 2) console.log(1) 複製代碼
咱們能夠發現,運行的時候才知道咱們出現了參數的問題,那麼這未嘗不是咱們頭疼的一個問題呢,就像是
undefined.fliter() 複製代碼
本節小結
能夠簡單的體會到如下幾點
/** * * @param {string} name * @param {number} age * @param {string} sex */ const Me = (name, age, sex) => `個人名字是${name},我今年${age},我是${sex}生,歡迎關注個人掘金呦~` console.log(Me(`洋小洋同窗`, 18, `男`)) =>個人名字是洋小洋同窗,我今年18,我是男生,歡迎關注個人掘金呦~ 複製代碼
老規矩,我們一塊寫個hello world
const hello = (): string => `hello TS` 複製代碼
咱們知道瀏覽器是不支持ts
代碼的(目前),那麼咱們該如何編輯一下呢
> npm install -g typescript
複製代碼
+ typescript@3.7.5 這樣咱們就能夠經過tsc xxx.ts
來進行編譯爲js
代碼
var hello = function () { return "hello TS"; }; // js代碼 複製代碼
這裏咱們能夠經過tsc -w xx.ts
來監聽ts
文件的變化並編譯
在咱們基礎調試的過程當中,會遇到 was also declared here
這裏我們知道:JS數據類型
分類和判斷 JavaScript中有6種數據類型:數字(number)、字符串(string)、布爾值(boolean)、undefined、null、對象(Object)。 其中對象類型包括:數組(Array)、函數(Function)、還有兩個特殊的對象:正則(RegExp)和日期(Date)。 那麼在ts
中,定義變量
let a: number a = 123 // 其中a 只能夠是number 類型 複製代碼
在ts
中有一種類型,叫作任意類型any
any
let list: any[] = [1, true, "free"]; list[1] = 100; 複製代碼
const myInfoFn = (info: any) => { switch (typeof info) { case `number`: console.log(`我今年${info}`) case `string`: console.log(`個人名字${info}`) default: } } myInfoFn(`yayxs`) 複製代碼
在實際的開發中,常常打交道的即是數組
,那麼在ts
中有兩種經常使用定義數組的方式(其中數組的每項元素是同一類型)
let arrSt: string[] = [`yayxs`] // 每一項是string let arrNum: Array<number> = [1, 2, 3] // 每一項是number 複製代碼
let tuple: [string, number, string] tuple = [`yayxs`, 18, `男生`] console.log(`${tuple[0]}是${tuple[2]},今年${tuple[1]}`) 複製代碼
函數無非是參數``返回值
等,其中包括默認參數,可選參數等
const addFn = (a: number, b: number): number => a + b const addCon = (a: number, b: number): void => console.log(a+b) // 函數沒有返回值 const addFn = (a: number, b: number = 10): number => a + b // 參數默認值 b默認值值10 複製代碼
可選參數
const addABC = (a: number, b: number, c?: number): number => { if (c) { return a + b + c } else { return a + b } } 複製代碼
不肯定參數 口語中的不肯定參數又叫剩餘參數
,示例:
const arrList: Array<number> = [1, 2, 3, 4]; // 其中acc爲累加器;cur當前值 const reducer = (acc, cur): number => acc + cur; let res: number = arrList.reduce(reducer, 5) console.log(res) // 15 const addFun = (a: number, ...nums: number[]) => nums.reduce(reducer, a) console.log(addFun(1, 2, 3, 4)) // 10 複製代碼
class MyGirlFriend { // 定義數據內容 name: string; age: number; height: string } // new 對象 let xuehua = new MyGirlFriend() // set 內容 xuehua.name = `xuehua` // 雪花 // get 內容 console.log(xuehua.name) 複製代碼
class MyGirlFriend { // 定義數據內容 name: string; age: number; height: string constructor(name: string, age: number, height: string) { this.name = name this.age = age this.height = height } formatHeight() { return `${this.height} cm` } } // new 對象 let xuehua = new MyGirlFriend(`xuehua`, 20, `172`) console.log(xuehua.name) // xuehua console.log(xuehua.age) // 20 console.log(xuehua.formatHeight()) // 172cm 複製代碼
咱們能夠看下編譯爲js
以後的代碼
var MyGirlFriend = /** @class */ (function () { function MyGirlFriend(name, age, height) { this.name = name; this.age = age; this.height = height; } MyGirlFriend.prototype.formatHeight = function () { return this.height + " cm"; }; return MyGirlFriend; }()); // new 對象 var xuehua = new MyGirlFriend("xuehua", 20, "172"); console.log(xuehua.name); console.log(xuehua.age); console.log(xuehua.formatHeight()); 複製代碼
咱們都知道面向對象``繼承``多態
,那麼什麼是繼承嘞:
/// 繼承Demo class Person { // 數據-屬性 name: string age: number constructor(name: string, age: number) { this.name = name this.age = age } // 行爲-方法 sayHi() { console.log(`HI`) } } // 繼承Person class Programmer extends Person { sayNo() { console.log(`NO`) } sayHi() { // console.log(`en~~~`) super.sayHi() // 調用父類 } } let p = new Programmer(`yayxs`, 18) p.sayHi() 複製代碼
相應的js
代碼
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); /// 繼承Demo var Person = /** @class */ (function () { function Person(name, age) { this.name = name; this.age = age; } // 行爲-方法 Person.prototype.sayHi = function () { console.log("HI"); }; return Person; }()); // 繼承Person var Programmer = /** @class */ (function (_super) { __extends(Programmer, _super); function Programmer() { return _super !== null && _super.apply(this, arguments) || this; } Programmer.prototype.sayNo = function () { console.log("NO"); }; Programmer.prototype.sayHi = function () { // console.log(`en~~~`) _super.prototype.sayHi.call(this); }; return Programmer; }(Person)); var p = new Programmer("yayxs", 18); p.sayHi(); 複製代碼
抽象類不可以
被實例化,可是能夠被繼承,一般要完成方法的實例化
// 抽象類 abstract class Person { name: string constructor(name: string) { this.name = name } sayHi(name: string): void { console.log(`hi`) } // 抽象方法 沒有方法體 abstract work(): void } // let p = new Person() err class Coder extends Person { sayHi() { console.log(`12131`) } work() { console.log(`i am working`) } } 複製代碼
public
private
protected
其實在類的內部,全部的屬性
和方法
默認是經過public
修飾符來進行修飾的
class Person { public name:string // 默認都是公開的 } 複製代碼
關於ts
中的修飾符在其餘語言中也是相似的,例如Java
等。
// 公共,私有與受保護的修飾符 class Person { public name: string private age: string public sayName() { console.log(`my name is ${this.name}`) } private sayAge() { console.log(`my age is ${this.age}`) } } 複製代碼
那麼經過private
修飾的方法,在外部該怎麼訪問到呢?
getName() { console.log(this.name) } // 獲取名字 let p = new Person() p.getName() // undefined 複製代碼
getName() { console.log(this.name) } setName(name: string) { this.name = name } let p = new Person() p.setName(`yayxs`) p.getName() // yayxs 複製代碼
當被protected
修飾後,是不能new
的,也就是說不能實例化
經過static
修飾的屬性或者方法,能夠直接經過類.xxx
,
class Person { public static _name: string; public static getName(): string { return Person._name } } console.log(Person.getName()) 複製代碼
本節小結
在ts
中類的實踐仍是十分有意義的,包括像靜態屬性方法,或者像public
protected
private
這些修飾符修飾在屬性以及方法前邊,主要是權限的限制,大體是 public
> protected
> private
具體的修飾細則,包括像 在父類以及子類的調用權限問題,能夠翻閱相關的文檔
new
出來的實例對象上,這點官網也有說到首先不得不提的是枚舉在實際開發的過程當中,仍是十分有益的,可以加強代碼的可讀性,筆者在封裝網絡請求類或者網頁中常見的下拉選擇框有用到
enum RequestType { GET, POST, PUT, DELETE } console.log(RequestType.DELETE) // 3 複製代碼
之於爲何是3
呢
var RequestType; (function (RequestType) { RequestType[RequestType["GET"] = 0] = "GET"; RequestType[RequestType["POST"] = 1] = "POST"; RequestType[RequestType["PUT"] = 2] = "PUT"; RequestType[RequestType["DELETE"] = 3] = "DELETE"; })(RequestType || (RequestType = {})); console.log(RequestType.DELETE); 複製代碼
接口在實際生活中也是無處不在的,像水龍頭與水管
插板與插槽
等等,那麼在程序中也是同樣的,同一接口規範了一類
// 接口 interface HasName { name: string } // 定義對象 const obj = { name: `yayxs` } // 定義方法 const sayName = (o: HasName) => { console.log(`my name is ${o.name}`) } sayName(obj) // my name is yayxs 複製代碼
以上的接口示例是隻有參數,那接口裏也是能夠定義方法的,就像這樣
// 接口 interface HasName { name: string printName(name: string): void } // 定義對象 const obj = { name: `yayxs`, printName: (name: string) => { console.log(name) } } // 定義方法 const sayName = (o: HasName) => { console.log(`my name is ${o.name}`) o.printName(o.name) // yayxs } sayName(obj) 複製代碼
在定義接口的時候,咱們能夠定義一部分可選擇的屬性或方法,在實現接口的時候不是非要實現
/// 可選參數 type PerInfo = string interface Person { name: PerInfo age?: number sex: PerInfo printName(): void printAge?(): void } // 我實現人類接口 class Me implements Person { printName(): void { console.log(`xxx`) } name: string sex: string } let m = new Me() 複製代碼
類型別名,顧名思義就是類型的別名
// 類型別名 type Name = string; // 把string別名爲Name 更語義化一點 const myName: Name = `yayxs` console.log(myName) 複製代碼
又或者
type User = { name: string, age: number, sex: string } const me: User = { name: `yayxs`, age: 18, sex: `nan` } console.log(me) 複製代碼
那麼類型別名
和接口
有點類似
// 定義支付接口 interface Pay { doSomething(): void // 支付接口方法 } const myPay = () => { // 實現接口中的pay() doSomething: () => { console.log(`實現支付動做`) } } // 定義不一樣的類來實現接口 class WxPay implements Pay { doSomething() { console.log(`我是微信支付`) } } class AlPay implements Pay { doSomething() { console.log(`我是支付寶支付`) } } let weixin_pay: Pay = new WxPay() let ali_pay: Pay = new AlPay() 複製代碼
在一些接口的內部聲明的函數是沒有名字的
// 匿名函數接口 interface Person { (num: number): void } let printNum: Person; printNum = (num: number) => { console.log(num) } 複製代碼
包括像是在dart
語言內,都會有斷言存在
// 類型斷言 let x: any = `hello` let res = (<string>x).substring(0, 1) console.log(res) 複製代碼
在實際開發中書寫斷言幾種常見的方式
interface Person { name: string age: number } let me = {} as Person me.name = `yayxs` me.age = 18 複製代碼
let you = <Person>{ name: `xuehua`, age: 17 } 複製代碼
類不能繼承多個父類,可是能夠實現多個已定義的接口
interface Person { name: string } interface Coder { age: number } class Per implements Person, Coder { name: string age: number } 複製代碼
// 可索引類型 interface Istr { [index: string]: string } let myStr: Istr; myStr = { 'name': `yayxs` } interface Inum { [index: number]: string } let myNum: Inum myNum = [`12`] 複製代碼
在如上第二個例子中,有點像數組的定義方式,那與數組有什麼不一樣呢
length
或者push
等常見的方法在其餘的編程語言中,獲取屬性與設置也一樣的有這種狀況 在前面部分咱們提到過
這樣的話,就遇到一個問題,沒法讀取name
屬性,這時候能夠經過內部方法暴露出來,那一樣在類中有get
set
// setter and getter class Person { private name: string private age: number constructor(age: number, name: string) { this.name = name this.age = age } get getName(): string { return this.name } set setName(name: string) { this.name = name } } let p = new Person(18, `yayxs`) // console.log(p.name) 複製代碼
在js
中是沒有函數重載的,那麼在ts
中什麼是函數重載呢,
// 函數重載 function hello(name: string): string function hello(age: number): number function hello(params: any): any { if (typeof params === `string`) { return params } else if (params === `number`) { return params } } 複製代碼
咱們能夠經過!
來進行非空的檢查
let str: string = `yayxs`; str!.substring(0, 1) 複製代碼
function loop():never{ while(true){ } } 複製代碼
function err(): never { throw Error } 複製代碼
在其餘的語言中像JAVA
中有泛型的概念,
// 泛型 function getArr<T>(params: T[]): T[] { return params } console.log(getArr<number>([22, 1])) class Person<S, N> { private _name: S private _age: N constructor(name: S, age: N) { this._name = name this._age = age } get name(): S { return this._name } get age(): N { return this._age } } let p = new Person<string, number>(`yaxs`, 18) console.log(p.name) 複製代碼
在編譯的時候請使用
tsc --target es5 demo12.ts
複製代碼
能夠經過兩種方式導出ts
中的元素
能夠經過tsc --init
來生成這麼一個文件
{
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
複製代碼
具體的註釋的含義,能夠參考官方文檔
若是你尚未看過癮,在19年度
我曾寫過一篇很簡單的文章,也是基於第三方的環境搭建的一篇極爲簡單的博客(依賴github、Vuepress)
相關代碼已經上傳至 筆者github ,也歡迎指正不恰當的地方,感謝~