因爲在春節期間,實在沒什麼事幹,就想系統的寫份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 ,也歡迎指正不恰當的地方,感謝~