目錄
五、TypeScript 不適合在 vue 業務開發中使用嗎?
六、Property 'name' has no initializer
一、快速上手
TypeScript
是 JavaScript
的強類型版本。然後在編譯期去掉類型和特有語法,生成純粹的 JavaScript
代碼。
TypeScript
是 JavaScript
的超集,這意味着他支持所有的 JavaScript
語法。
強類型語言的優勢在於靜態類型檢查。
TypeScript是微軟開發的語言。
vue3.0使用ts開發。
是github開源項目:https://github.com/Microsoft/TypeScript
2012 年 10 月誕生。
vscode是用ts編寫的:https://github.com/Microsoft/vscode/
裝包:
yarn global add typescript
檢查版本:
tsc -V
初始化:
tsc --init
index.ts:
function greeter(person) {
return "Hello, " + person;
}
let user = "Jane User";
document.body.innerHTML = greeter(user);
編程成js文件:
tsc index.ts
index.js:
function greeter(person) {
return "Hello, " + person;
}
var user = "Jane User";
document.body.innerHTML = greeter(user);
類型檢查:
如果函數參數聲明是字符串,卻傳了數字,會有警告信息
function greeter(person:string) {
return "Hello, " + person;
}
let user = 1;
document.body.innerHTML = greeter(user);
及時不傳參數也會報錯:
interface接口:
定義Person包含的字段
interface Person {
firstName: string;
lastName: string;
}
function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = { firstName: "Jane", lastName: "User" };
document.body.innerHTML = greeter(user);
使用class創建類:
class Student {
fullName: string;
constructor(public firstName, public middleInitial, public lastName) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
}
interface Person {
firstName: string;
lastName: string;
}
function greeter(person : Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = new Student("Jane", "M.", "User");
document.body.innerHTML = greeter(user);
編譯後是:
var Student = /** @class */ (function () {
function Student(firstName, middleInitial, lastName) {
this.firstName = firstName;
this.middleInitial = middleInitial;
this.lastName = lastName;
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
return Student;
}());
function greeter(person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
var user = new Student("Jane", "M.", "User");
document.body.innerHTML = greeter(user);
訪問網頁,index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./index.js"></script>
</body>
</html>
效果:
自動補全功能:
二、vue的TS
引入Vue文件的時候需要加上.vue
後綴,否則編輯器識別不到
TS路由:
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes: Array<RouteConfig> = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
Vue.use(VueRouter)
const routes: Array<RouteConfig> = [
{
path: '/',
redirect: '/login'
},
{
path: '/login',
component: () => import('../views/Login.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
三、使用vue-class-component
https://class-component.vuejs.org/
加加減減:
<template>
<div>
<div>{{count}}</div>
<button @click="handleSub">減</button>
<button @click="handleAdd">加</button>
</div>
</template>
<script>
import Vue from 'vue'
import Component from 'vue-class-component'
@Component
class Login extends Vue {
count = 0
handleSub() {
this.count--
}
handleAdd() {
this.count++
}
}
export default Login
</script>
或者:
<template>
<div>
<div>{{count}}</div>
<button @click="handleSub">減</button>
<button @click="handleAdd">加</button>
</div>
</template>
<script>
import Vue from 'vue'
import Component from 'vue-class-component'
@Component
class Login extends Vue {
data() {
return {
count: 0
}
}
handleSub() {
this.count--
}
handleAdd() {
this.count++
}
}
export default Login
</script>
v-model:
<template>
<div>
<input v-model="username">
</div>
</template>
<script>
import Vue from 'vue'
import Component from 'vue-class-component'
@Component
class Login extends Vue {
username = 'admin'
}
export default Login
</script>
掛載完聲明週期:
<template>
<div>
1
</div>
</template>
<script>
import Vue from 'vue'
import Component from 'vue-class-component'
@Component
class Login extends Vue {
mounted () {
console.log('掛載完')
}
}
export default Login
</script>
計算屬性:
<template>
<div>
{{double}}
</div>
</template>
<script>
import Vue from 'vue'
import Component from 'vue-class-component'
@Component
class Login extends Vue {
count = 1
get double() {
return this.count * 2
}
}
export default Login
</script>
父子組件傳值:
父組件
<template>
<div>
<Icon :name="visible ? 'xianshimima' : 'buxianshimima'" @onClick="handleVisible"></Icon>
</div>
</template>
<script>
import Vue from 'vue'
import Component from 'vue-class-component'
import Icon from '../components/Icon'
@Component({
components: {
Icon
}
})
class Login extends Vue {
visible = false
handleVisible() {
this.visible = !this.visible
}
}
export default Login
</script>
子組件
<template>
<span :class="[`icon iconfont icon-${name}`]" @click="handleClick"></span>
</template>
<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
@Component
class Icon extends Vue {
@Prop()
name: string
handleClick() {
this.$emit('onClick')
}
}
export default Icon
</script>
子組件也可以寫成這種
<template>
<span :class="[`icon iconfont icon-${name}`]" @click="handleClick"></span>
</template>
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
const IconProps = Vue.extend({
props: {
name: String
}
})
@Component
class Icon extends IconProps {
handleClick() {
this.$emit('onClick')
}
}
export default Icon
</script>
四、vue-property-decorator
vue屬性裝飾器
父子組件傳值,傳參
父組件:
<template>
<div>
<Icon :name="visible ? 'show' : 'hide'" @onClick="handleVisible"></Icon>
</div>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
import Icon from '../components/Icon.vue'
@Component({
components: {
Icon
}
})
class Login extends Vue {
visible = false
handleVisible(payload:object) {
this.visible = !this.visible
}
}
export default Login
</script>
子組件:
<template>
<span :class="[`icon iconfont icon-${name}`]" @click="handleClick"></span>
</template>
<script lang="ts">
import { Vue, Component, Prop, Emit } from 'vue-property-decorator'
@Component
class Icon extends Vue {
@Prop({ default: 'zhanwei' })
name: string
@Emit('onClick')
handleClick() {
return { id: 2 }
}
}
export default Icon
</script>
五、TypeScript 不適合在 vue 業務開發中使用嗎?
https://www.zhihu.com/question/310485097/answer/591869966
六、Property 'name' has no initializer
Property 'name' has no initializer and is not definitely assigned in the constructor.
解決辦法一:
把tsconfig.json文件裏的strict字段改成false
解決辦法二:
在屬性名後面加歎號,這是一種修飾符,標識忽略
<template>
<span :class="[`icon iconfont icon-${name}`]" @click="handleClick"></span>
</template>
<script lang="ts">
import { Vue, Component, Prop, Emit } from 'vue-property-decorator'
@Component
class Icon extends Vue {
@Prop({ default: 'zhanwei' })
name!: string
@Emit('onClick')
handleClick() {
return { id: 2 }
}
}
export default Icon
</script>
七、下載量對比
八、tsconfig.json
ts的配置
{
"compilerOptions": {
"target": "esnext", //編譯的目標版本
"module": "esnext", //指定生成哪個模塊系統代碼
"strict": true, //靜態類型檢查
"jsx": "preserve", //
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true, // 是否生成map文件
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
九、shims-vue.d.ts
shims: 墊片
由於 TypeScript 默認並不支持 *.vue 後綴的文件,所以在 vue 項目中引入的時候需要創建一個shims-vue.d.ts 文件,放在項目應使用目錄下,例如 src/shims-vue.d.ts,用來支持*.vue 後綴的文件;
主要用於 TypeScript 識別.vue 文件,Ts默認並不支持導入 vue 文件,這個文件告訴ts 導入.vue 文件都按VueConstructor<Vue>處理
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
十、shims-tsx.d.ts
允許你以.tsx結尾的文件,在Vue項目中編寫jsx代碼
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any;
}
}
}
十一、vuex-class
https://www.npmjs.com/package/vuex-class
使用倉庫做加加減減
倉庫:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
interface Payload {
key: string,
value: any
}
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
setState(state:any, payload:Payload) {
state[payload.key] = payload.value
}
},
actions: {
},
modules: {
}
})
頁面:
<template>
<div>
<div>{{count}}</div>
<button @click="handleSub">減</button>
<button @click="handleAdd">加</button>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import { State, Mutation } from 'vuex-class'
@Component
class Login extends Vue {
@State('count') count!:number
@Mutation('setState') setState!:Function
handleSub() {
let count = this.count - 1
this.setState({ key: 'count', value: count })
}
handleAdd() {
let count = this.count + 1
this.setState({ key: 'count', value: count })
}
}
export default Login
</script>
十二、ts
初始化:
在空文件夾裏打開終端 -> 輸入tsc --init 自動生成tsconfig.json
{
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', 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": "./js", //輸出文件夾 /* 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. */
}
}
監視ts文件變化,自動編譯:
vscode -> 終端 -> 運行任務 -> typescript -> tsc: 監視 - (tsconfig.json的目錄)
課堂練習(1):
function test(name:string) {
console.log(name)
}
test('hello3')
//元組
let tuple:[string, number, string] = ['a', 1, 'b']
console.log(tuple)
//任意值
let person:any = 'xu'
console.log(person)
//四要素:調用, 參數,返回值,作用
function fun(name:string):string {
console.log(name)
return name
}
fun('xu')
//never 不可到達
const error = ():never => {
throw new Error('錯誤')
}
//error()
const loop = () => {
while(true);
}
//loop()
console.log(1)
//枚舉
enum Color { Red = 2, Green, Blue }
console.log(Color.Red) //2
enum obj {
None,
Read,
G = '123'.length
}
console.log(obj)
enum Enum {
A
}
let a = Enum.A
console.log(a) //0
let na = Enum[a]
console.log(na) //A
課堂練習(2):
function sayHello(person: string) {
return 'Hello,' + person
}
let user:string = 'a'
console.log(sayHello(user))
let num:number = 1
//沒有顯示聲明類型,則會進行類型推斷
let a = 'a'
a = 'b' //賦值其他類型,則會報錯
let flag:boolean = true
//數字類型的數組
let arr:number[] = [1,2]
let strArr:string[] = ['a', 'b']
let objArr:object[] = [{}]
//泛型方式
let arr1:Array<number> = [1]
let strArr1:Array<string> = ['a','b']
//元組
let arr2:[number, string, boolean] = [1, '2', true]
//null, undefined
let my_null:null = null
let my_undefined:undefined = undefined
//可以是字符串也可以是undefined
let c:string | undefined
console.log(c)
//never 從來不會出現的值
// let my_never: never = (() => {
// throw new Error()
// })()
//any 任何類型
let my_any:any = '任何類型'
console.log(my_any)
//任何類型的數組
let arrAny:any[] = ['a', 1, true]
function run():void {
console.log('run')
}
run()
function getName():string {
return 'xu'
}
console.log(getName())
const myRun = ():void => {
console.log('myRun')
}
myRun()
const myGetName = ():string => {
return 'myGetName'
}
console.log(myGetName())
//構造函數Boolean 創造的不是布爾值
//let myBoolean:boolean = new Boolean(1)
課堂練習(3):
//函數
//沒有返回值是用void
function getInfo(name:string, age:number):string {
return `我叫${name},今年${age}歲。`
}
console.log(getInfo('xu', 30))
let getInfo1 = (name:string, age:number):string => {
return `我叫${name},今年${age}歲。`
}
console.log(getInfo('xu', 31))
//完整類型
let getInfo2:(name:string, age:number) => string = (name:string, age:number):string => {
return `我叫${name},今年${age}歲。`
}
let getInfo3:Function = (name:string, age:number):string => {
return `我叫${name},今年${age}歲。`
}
//函數可選參數,加個問好
function myInfo(name: string, age?: number):string {
if (typeof age === 'number') {
return `我叫${name},今年${age}歲。`
} else {
return `我叫${name}`
}
}
console.log(myInfo('xu'))
//默認值
function myInfo1(name: string, age:number = 32):string {
if (typeof age === 'number') {
return `我叫${name},今年${age}歲。`
} else {
return `我加${name}`
}
}
console.log(myInfo1('xu'))
//求和
function sum(a:number, b:number):number {
return a + b
}
console.log(sum(1, 2))
//剩餘參數
function sum1(...rest:number[]):number {
console.log(rest) //數組
return rest.reduce((prev, item) => {
return prev + item
}, 0)
}
console.log(sum1(1, 2, 3, 4, 5))
//枚舉類型
// 0: 女, 1:男, 2:保密
enum Sex {
gril,
boy,
secret
}
let xu:Sex = Sex.boy
console.log(xu)
let xu1:Sex = 3
console.log(xu1)
function getSex(sex:Sex):string {
if (sex === Sex.gril) {
return '我是女孩'
} else if (sex === Sex.boy) {
return '我是男孩'
} else {
return '保密'
}
}
console.log(getSex(xu))
class A {
v:number = 100
test(num:number) {
if (num > this.v) {
console.log('大了');
} else if (num < this.v) {
console.log('小了')
} else {
console.log('對了')
}
}
}
let a = new A()
a.test(100)
let isDone:boolean = false
let isDone1:object = new Boolean(1)
let isDone2:Boolean = new Boolean(1)
let isDone3:boolean = Boolean(1)
//變量如果在聲明的時候,未指定其類型,那麼它會被識別爲任意值類型
let something //let something:any
something = 1
something = 'a'
//聯合類型
let myA: string | number
myA = 'a'
myA = 1
function getLength(something: string | []):number {
return something.length
}
let myB: string | number
myB = 'A'
myB.length
myB = 1
//myB.length //報錯
let arr:number[] = [1, 3, 2]
arr.sort((a, b) => {
return a - b
})
console.log(arr)
function is(ar:string,sr:string):boolean {
let result = true
if(ar.length===sr.length){
for(let i=0;i<ar.length;i++){
if(ar.indexOf(sr[i])===-1){
result = false
}
}
result = true
} else {
result = false
}
return result
}
console.log(is("asa","aas") )
function isAng(arr:string,trr:string):boolean{
if(arr.length===trr.length){
for(var i=0;i<arr.length;i++){
if(arr.indexOf(trr[i])===-1){
return false
}
}
return true
}
return false
}
console.log(isAng('listen', 'silenta'))
課堂練習(4):
//函數重載
function add(a:string, b:string):string;
function add(a:number, b:number):number;
function add(a:any, b:any): any {
if (typeof a === 'string') {
return a + '---' + b
} else {
return a + b
}
}
console.log(add('a', 'b'))
//類
//修飾符 pubulic protected private
//靜態屬性 static
class Person {
public name: string
protected age: number
static height: number = 170
constructor(name:string, age:number) {
this.name = name
this.age = age
console.log(Person.height, Person.getHeight())
}
protected getName():string {
return `我的名字叫${this.name}`
}
static getHeight():number {
return this.height
}
}
let xu = new Person('xu', 30)
//console.log(xu.getName())
console.log(Person.getHeight())
// class Animal {
// name:string
// constructor(name:string) {
// this.name = name
// }
// sayHi() {
// return `My name is ${this.name}`
// }
// }
// let a = new Animal('jack')
// console.log(a.sayHi())
//繼承
class Programmer extends Person {
job:string
constructor(name:string, age:number, job:string) {
super(name, age)
this.job = job
console.log(this.age)
}
getJob() {
return `${this.getName()},年齡${this.age},我的工作是${this.job}`
}
}
let xu1 = new Programmer('徐同保', 30, 'web前端')
//console.log(xu1.getName())
console.log(xu1.getJob())
//console.log(xu1.age)
//抽象類
abstract class Animal {
name: string
constructor(name: string) {
this.name = name
}
getName():string {
return this.name
}
abstract eat():void
}
class Cat extends Animal {
food: string
constructor(name: string, food: string) {
super(name)
this.food = food
}
eat():void {
console.log(`${this.getName()}愛吃魚`)
}
}
let ketty = new Cat('小貓', '魚')
ketty.eat()
//更簡練的寫法,不用定義name了,也不需要賦值
class MyAnimal {
constructor(public name:string) {
}
getName() {
return this.name
}
}
let myAnimal = new MyAnimal('小貓')
console.log(myAnimal.getName())
//接口
interface Goods {
name: string
price: number
flag: boolean
}
let cartList: Goods[] = [
{
name: '蘋果',
price: 8,
flag: true
},
{
name: '香蕉',
price: 5,
flag: false
}
]
function goodsInfo(goods:Goods) {
console.log(`${goods.name}現在${goods.price}元一斤${goods.flag ? ',正在促銷' : ''}`)
}
cartList.forEach(item => {
goodsInfo(item)
})
//函數接口
interface GoodsInfo {
(goods: Goods): string
}
let myGoodsInfo: GoodsInfo = (goods:Goods):string => {
return `${goods.name}現在${goods.price}元一斤${goods.flag ? ',正在促銷' : ''}`
}
//類接口,實現接口的時候使用implements(實現)
interface PersonI {
name: string,
age: number,
getName():string
}
interface WebI {
name: string,
age: number,
job: string
getName(): string
getJob(): string
}
//接口也可以繼承
interface WebIPlus extends PersonI {
job: string,
getJob(): string
}
class MyPerson implements PersonI {
name: string
age: number
constructor(name:string, age:number) {
this.name = name
this.age = age
}
getName() {
return this.name
}
}
class Web extends MyPerson implements WebIPlus {
job: string
constructor(name: string, age: number, job: string) {
super(name, age)
this.job = job
}
getJob() {
return this.job
}
getInfo() {
return `${this.name},${this.age},${this.job}`
}
}
let xu2 = new Web('xu', 30, 'web')
console.log(xu2.getInfo())
筆記:
TypeScript 中,使用 :
指定變量的類型,:
的前後有沒有空格都可以。
TypeScript 只會進行靜態檢查,如果發現有錯誤,編譯的時候就會報錯。
構造函數 Boolean
創造的對象不是布爾值。
當構造函數修飾爲 private
時,該類不允許被繼承或者實例化。
當構造函數修飾爲 protected
時,該類只允許被繼承,不允許實例化。
只讀屬性關鍵字,只允許出現在屬性聲明或索引簽名或構造函數中。
一個類只能繼承自另一個類,但是可以實現多個接口。
參考鏈接:
一起來擁抱強大的TypeScript吧--Ts+Vue完全教程