, 个人觉得写得很清晰。
8 |
9 | ### 1.1 类的创建
10 |
11 | es5类在构造函数和原型链里都可以添加属性和方法,原型链上的属性会被多个实例所共享,而构造函数则不会。
12 |
13 | ```js
14 |
15 | function Person() {
16 | this.name = 'Ming'
17 | this.run = function() {
18 | console.log(this.name + '在运动')
19 | }
20 | }
21 |
22 | Person.prototype.sex = '男' // 原型链上的属性会被多个实例所共享
23 | Person.prototype.work = function() {
24 | console.log(this.name + '在工作')
25 | }
26 |
27 |
28 | var p = new Person()
29 | p.run()
30 | p.work()
31 | console.log(p.name)
32 |
33 | ```
34 |
35 | ### 1.2 静态方法
36 |
37 | > 调用静态方法不需要实例化
38 |
39 | ```js
40 |
41 | Person.getInfo=function(){
42 | console.log('我是静态方法');
43 | }
44 | Person.getInfo();
45 |
46 | ```
47 |
48 | ### 1.3 实现继承
49 |
50 | > 对象冒充(或者叫构造函数继承)继承:可以继承构造函数里面的属性和方法,但是没法继承原型链上面的属性和方法
51 |
52 | > 原型继承:可以继承构造函数里面的属性和方法,也可以继承原型链上面的属性和方法,但是实例化子类的时候没法给父类传参
53 |
54 | 下面是通过**对象冒充 + 原型链**组合继承,解决了上面两种继承方式存在的问题
55 |
56 | ```js
57 |
58 | function Worker(name,age){
59 | this.name=name; /*属性*/
60 | this.age=age;
61 | this.run=function(){ /*实例方法*/
62 | alert(this.name+'在运动');
63 | }
64 |
65 | }
66 | Worker.prototype.sex="男";
67 | Worker.prototype.work=function(){
68 | alert(this.name+'在工作');
69 | }
70 |
71 | function Web(name,age){
72 | Worker.call(this,name,age); // 对象冒充继承,可以继承构造函数里面的属性和方法,实例化子类可以给父类传参
73 | }
74 | // Web.prototype = new Worker(); // 原型链继承方法一:继承Worker构造函数和原型上所有的方法和属性
75 | Web.prototype = Worker.prototype; //原型链继承方法二:优化了方法一重复继承构造函数属性和方法的问题(本质可以看看http://caibaojian.com/javascript-object-5.html)
76 |
77 | var w = new Web('赵四',20);
78 | w.run();
79 | w.work();
80 |
81 | ```
82 |
83 | 从上面可以看出,对象冒充继承是在子类Web构造函数里面通过call方法继承父类Worker的构造函数的属性和方法;原型链继承通过子类Web的原型对象等于父类Worker的原型对象来实现继承;最后这两种继承的组合方式实现了完美继承。
84 |
85 | ## typescript中的类
86 |
87 | 内容概述: ts中类的定义、继承、类修饰符、静态属性和静态方法、多态、抽象类和抽象方法
88 |
89 | ### 2.1 ts中类的定义
90 |
91 | ts中类的定义和es6类的定义一样
92 |
93 | ```js
94 |
95 | class PersonDefine {
96 | name: string // 属性,前面省略了public关键词
97 | constructor(name:string) { //构造函数
98 | this.name = name
99 | }
100 | run():string { // 原型
101 | return `${this.name}在运动`
102 | }
103 | }
104 | var define = new PersonDefine('类的定义')
105 | alert(define.run())
106 |
107 | ```
108 |
109 | ### 2.2 继承
110 |
111 | > ts中继承比es5简单很多,用extends super实现继承
112 |
113 | ```js
114 |
115 | class WebExtend extends PersonDefine {
116 | constructor(name:string) {
117 | super(name) // super继承父类的构造函数,并向父类构造函数传参
118 | }
119 | work():string {
120 | return `${this.name}在工作`
121 | }
122 | }
123 |
124 | var extend = new WebExtend('继承')
125 | alert(extend.run())
126 | alert(extend.work())
127 |
128 | ```
129 |
130 | ### 2.3 ts类里面的修饰符
131 |
132 | 修饰符:typescript里面定义属性的时候给我们提供了三种修饰符
133 |
134 | * public: 公有修饰符,在当前类里面、子类、类外面都可以访问
135 | * protected:保护类型,在当前类里面、子类里面可以访问,在类外部没法访问
136 | * private :私有修饰符,在当前类里面可以访问,子类、类外部都没法访问
137 |
138 | 注意:属性如果不加修饰符,默认就是公有修饰符
139 |
140 | ```js
141 |
142 | // 以private为例
143 | class PersonPrivate{
144 | private name:string; /*被private修饰的属性 => 私有属性*/
145 | constructor(name:string){
146 | this.name=name;
147 | }
148 | run():string{
149 | return `${this.name}在运动` // 私有属性只能在当前类里面可以访问
150 | }
151 | }
152 |
153 | class Web extends PersonPrivate{
154 | constructor(name:string){
155 | super(name)
156 | }
157 | work(){
158 | // return `${this.name}在工作` // 报错,子类不能访问父类的私有属性
159 | }
160 | }
161 |
162 | var privateName = new PersonPrivate('private')
163 | alert(privateName.run())
164 | // console.log(privateName.name) // 报错,外部不能访问类的私有属性
165 |
166 | ```
167 |
168 | ### 2.4静态属性和静态方法
169 |
170 | > 为什么要用静态属性和静态方法?jq里面的$.ajax就是用的静态方法
171 |
172 | ```js
173 |
174 | function $(element) {
175 | return new Base(element)
176 | }
177 |
178 | function Base(element) {
179 | this.element = document.getElementById(element)
180 | this.css = function(arr, value) {
181 | this.element.style[arr] = value
182 | }
183 | }
184 | $('box').css('color','red')
185 | $.ajax = function() {} // 想要在$上使用方法怎么办,用静态方法
186 |
187 | ```
188 |
189 | > ts中实现静态属性和静态方法用static
190 |
191 | ```js
192 |
193 | class PersonStatic{
194 | /*公有属性*/
195 | public name:string;
196 | constructor(name:string) {
197 | this.name=name;
198 | }
199 | /*实例方法(需要被实例化,所以为实例方法)*/
200 | run(){
201 | return `${this.name}在运动`
202 | }
203 | /*静态属性*/
204 | static sex = '男'
205 | /*静态方法,里面没法直接调用类里面的属性*/
206 | static info(){
207 | // return 'info方法' + this.name // 静态方法不能调用本类的方法和属性,可以调用静态属性
208 | return 'info方法' + PersonStatic.sex
209 | }
210 | }
211 |
212 | console.log('静态方法' + PersonStatic.info())
213 | console.log('静态属性' + PersonStatic.sex)
214 |
215 | ```
216 |
217 | ### 2.5多态
218 |
219 | > 父类定义一个方法不去实现,让继承它的子类去实现,每一个子类的该方法有不同的表现
220 |
221 | * 多态属于继承
222 |
223 | 比如定义一个父类Animal,里面的eat方法不去实现,让子类Dog和Cat分别实现自己的eat方法
224 |
225 | ```js
226 |
227 | class Animal {
228 | name:string;
229 | constructor(name:string) {
230 | this.name=name;
231 | }
232 | eat(){ // eat方法继承它的子类去实现
233 | }
234 | }
235 | class Dog extends Animal{
236 | constructor(name:string){
237 | super(name)
238 | }
239 | eat(){
240 | return this.name+'吃粮食'
241 | }
242 | }
243 |
244 | class Cat extends Animal{
245 | constructor(name:string){
246 | super(name)
247 | }
248 | eat(){
249 | return this.name+'吃老鼠'
250 | }
251 | }
252 |
253 | ```
254 |
255 | ### 2.6抽象类和抽象方法
256 |
257 | > 定义:用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类(抽象类的子类)中实现
258 |
259 | * 抽象类:它是提供其他类继承的基类,不能直接被实例化,子类继承可以被实例化
260 | * abstract修饰的方法(抽象方法)只能放在抽象类里面
261 | * 抽象类和抽象方法用来定义标准(比如定义标准为:抽象类Animal有抽象方法eat,要求它的子类必须包含eat方法)
262 |
263 | ```js
264 |
265 | abstract class AnimalAbst{
266 | public name:string;
267 | constructor(name:string){
268 | this.name=name;
269 | }
270 | abstract eat():any; //抽象方法不包含具体实现并且必须在派生类中实现
271 | run(){
272 | console.log('其他方法可以不实现')
273 | }
274 | }
275 | // var a = new Animal() /*错误的写法,抽象类不能被实例化*/
276 |
277 | class DogAbst extends Animal{
278 | //抽象类的子类必须实现抽象类里面的抽象方法
279 | constructor(name:any){
280 | super(name)
281 | }
282 | eat(){
283 | return this.name + '吃粮食'
284 | }
285 | }
286 |
287 | var d = new DogAbst('小花花');
288 | console.log('抽象类和抽象方法',d.eat());
289 |
290 | ```
291 |
292 | 例子中源代码在当前文件夹
--------------------------------------------------------------------------------
/4.TypeScript类/e5中的类.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 | es5中的类、静态方法、继承(原型链继承,对象冒充继承,原型链 + 对象冒充组合继承)
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/4.TypeScript类/js/es5.js:
--------------------------------------------------------------------------------
1 | /**
2 | * es5中的类
3 | * 内容为:类的定义、静态方法、继承(对象冒充继承,原型链继承,对象冒充 + 原型链组合继承)
4 | * 了解本质看这个文档觉得很好http://caibaojian.com/javascript-object-5.html
5 | */
6 |
7 | /**
8 | * 1.类的定义
9 | * es5类在构造函数和原型链里都可以添加属性和方法,原型链上的属性会被多个实例所共享,而构造函数则不会
10 | */
11 | function Person() {
12 | this.name = 'Ming'
13 | this.run = function() {
14 | console.log(this.name + '在运动')
15 | }
16 | }
17 |
18 | Person.prototype.sex = '男' // 原型链上的属性会被多个实例所共享
19 | Person.prototype.work = function() {
20 | console.log(this.name + '在工作')
21 | }
22 |
23 |
24 | var p = new Person()
25 | p.run()
26 | p.work()
27 | console.log(p.name)
28 |
29 | // 2.静态方法
30 | Person.getInfo=function(){
31 | console.log('我是静态方法');
32 | }
33 |
34 | // 调用静态方法不需要实例化
35 | Person.getInfo();
36 |
37 | /**
38 | * 3.实现继承
39 | * 对象冒充(或者叫构造函数继承)继承:可以继承构造函数里面的属性和方法,但是没法继承原型链上面的属性和方法
40 | * 原型继承:可以继承构造函数里面的属性和方法,也可以继承原型链上面的属性和方法,但是实例化子类的时候没法给父类传参
41 | */
42 |
43 | function Worker(name,age){
44 | this.name=name; /*属性*/
45 | this.age=age;
46 | this.run=function(){ /*实例方法*/
47 | console.log(this.name+'在运动');
48 | }
49 |
50 | }
51 | Worker.prototype.sex="男";
52 | Worker.prototype.work=function(){
53 | console.log(this.name+'在工作');
54 | }
55 |
56 | function Web(name,age){
57 | Worker.call(this,name,age); // 对象冒充继承,可以继承构造函数里面的属性和方法,实例化子类可以给父类传参
58 | }
59 | // Web.prototype = new Worker(); // 原型链继承方法一:继承Worker构造函数和原型上所有的方法和属性
60 | Web.prototype = Worker.prototype; //原型链继承方法二:优化了方法一重复继承构造函数属性和方法的问题(本质可以看看http://caibaojian.com/javascript-object-5.html)
61 |
62 | var w = new Web('赵四',20);
63 | w.run();
64 | w.work();
--------------------------------------------------------------------------------
/4.TypeScript类/js/typescript.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | /**
3 | * typescript中的类
4 | * 1.ts类的定义
5 | * 2.继承
6 | * 3.ts类里面的修饰符
7 | * 4.静态属性和静态方法
8 | * 5.多态
9 | * 6.抽象类和抽象方法
10 | */
11 | var __extends = (this && this.__extends) || (function () {
12 | var extendStatics = function (d, b) {
13 | extendStatics = Object.setPrototypeOf ||
14 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
15 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
16 | return extendStatics(d, b);
17 | }
18 | return function (d, b) {
19 | extendStatics(d, b);
20 | function __() { this.constructor = d; }
21 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
22 | };
23 | })();
24 | /**
25 | * 1.ts中类的定义
26 | * 和es6类的定义一样
27 | */
28 | var PersonDefine = /** @class */ (function () {
29 | function PersonDefine(name) {
30 | this.name = name;
31 | }
32 | PersonDefine.prototype.run = function () {
33 | return this.name + "\u5728\u8FD0\u52A8";
34 | };
35 | return PersonDefine;
36 | }());
37 | var define = new PersonDefine('类的定义');
38 | console.log(define.run());
39 | /**
40 | * 2.继承
41 | * 用extends super实现继承
42 | */
43 | var WebExtend = /** @class */ (function (_super) {
44 | __extends(WebExtend, _super);
45 | function WebExtend(name) {
46 | return _super.call(this, name) || this; // super继承父类的构造函数,并向父类构造函数传参
47 | }
48 | WebExtend.prototype.work = function () {
49 | return this.name + "\u5728\u5DE5\u4F5C";
50 | };
51 | return WebExtend;
52 | }(PersonDefine));
53 | var extend = new WebExtend('继承');
54 | console.log(extend.run());
55 | console.log(extend.work());
56 | /**
57 | * 3.ts类里面的修饰符
58 | * 修饰符:typescript里面定义属性的时候给我们提供了三种修饰符
59 | * public:公有修饰符,在当前类里面、子类、类外面都可以访问
60 | * protected:保护类型,在当前类里面、子类里面可以访问,在类外部没法访问
61 | * private :私有修饰符,在当前类里面可以访问,子类、类外部都没法访问
62 | * 属性如果不加修饰符,默认就是公有修饰符
63 | */
64 | // 以private为例
65 | var PersonPrivate = /** @class */ (function () {
66 | function PersonPrivate(name) {
67 | this.name = name;
68 | }
69 | PersonPrivate.prototype.run = function () {
70 | return this.name + "\u5728\u8FD0\u52A8"; // 私有属性只能在当前类里面可以访问
71 | };
72 | return PersonPrivate;
73 | }());
74 | var Web = /** @class */ (function (_super) {
75 | __extends(Web, _super);
76 | function Web(name) {
77 | return _super.call(this, name) || this;
78 | }
79 | Web.prototype.work = function () {
80 | // return `${this.name}在工作` // 报错,子类不能访问父类的私有属性
81 | };
82 | return Web;
83 | }(PersonPrivate));
84 | var privateName = new PersonPrivate('private');
85 | console.log(privateName.run());
86 | // console.log(privateName.name) // 报错,外部不能访问类的私有属性
87 | /**
88 | * 4.静态属性和静态方法
89 | * 为什么要用静态属性和静态方法?jq里面的$.ajax就是用的静态方法
90 | */
91 | /*
92 | function $(element) {
93 | return new Base(element)
94 | }
95 |
96 | function Base(element) {
97 | this.element = document.getElementById(element)
98 | this.css = function(arr, value) {
99 | this.element.style[arr] = value
100 | }
101 | }
102 | $('box').css('color','red')
103 | $.ajax = function() {} // 想要在$上使用方法怎么办,用静态方法
104 | */
105 | // ts中实现静态属性和静态方法用static
106 | var PersonStatic = /** @class */ (function () {
107 | function PersonStatic(name) {
108 | this.name = name;
109 | }
110 | /*实例方法(需要被实例化,所以为实例方法)*/
111 | PersonStatic.prototype.run = function () {
112 | return this.name + "\u5728\u8FD0\u52A8";
113 | };
114 | /*静态方法,里面没法直接调用类里面的属性*/
115 | PersonStatic.info = function () {
116 | // return 'info方法' + this.name // 静态方法不能调用本类的方法和属性,可以调用静态属性
117 | return 'info方法' + PersonStatic.sex;
118 | };
119 | /*静态属性*/
120 | PersonStatic.sex = '男';
121 | return PersonStatic;
122 | }());
123 | console.log('静态方法' + PersonStatic.info());
124 | console.log('静态属性' + PersonStatic.sex);
125 | /**
126 | * 5.多态
127 | * 父类定义一个方法不去实现,让继承它的子类去实现,每一个子类的该方法有不同的表现
128 | * 多态属于继承
129 | */
130 | // 比如定义一个父类Animal,里面的eat方法不去实现,让子类Dog和Cat分别实现自己的eat方法
131 | var Animal = /** @class */ (function () {
132 | function Animal(name) {
133 | this.name = name;
134 | }
135 | Animal.prototype.eat = function () {
136 | };
137 | return Animal;
138 | }());
139 | var Dog = /** @class */ (function (_super) {
140 | __extends(Dog, _super);
141 | function Dog(name) {
142 | return _super.call(this, name) || this;
143 | }
144 | Dog.prototype.eat = function () {
145 | return this.name + '吃粮食';
146 | };
147 | return Dog;
148 | }(Animal));
149 | var Cat = /** @class */ (function (_super) {
150 | __extends(Cat, _super);
151 | function Cat(name) {
152 | return _super.call(this, name) || this;
153 | }
154 | Cat.prototype.eat = function () {
155 | return this.name + '吃老鼠';
156 | };
157 | return Cat;
158 | }(Animal));
159 | /**
160 | * 6.抽象类和抽象方法
161 | * 抽象类:它是提供其他类继承的基类,不能直接被实例化,子类继承可以被实例化
162 | * 定义:用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类(抽象类的子类)中实现。
163 | * abstract修饰的方法(抽象方法)只能放在抽象类里面
164 | * 抽象类和抽象方法用来定义标准(比如定义标准为:抽象类Animal有抽象方法eat,要求它的子类必须包含eat方法)
165 | */
166 | var AnimalAbst = /** @class */ (function () {
167 | function AnimalAbst(name) {
168 | this.name = name;
169 | }
170 | AnimalAbst.prototype.run = function () {
171 | console.log('其他方法可以不实现');
172 | };
173 | return AnimalAbst;
174 | }());
175 | // var a = new Animal() /*错误的写法,抽象类不能被实例化*/
176 | var DogAbst = /** @class */ (function (_super) {
177 | __extends(DogAbst, _super);
178 | //抽象类的子类必须实现抽象类里面的抽象方法
179 | function DogAbst(name) {
180 | return _super.call(this, name) || this;
181 | }
182 | DogAbst.prototype.eat = function () {
183 | return this.name + '吃粮食';
184 | };
185 | return DogAbst;
186 | }(Animal));
187 | var d = new DogAbst('小花花');
188 | console.log('抽象类和抽象方法', d.eat());
189 |
--------------------------------------------------------------------------------
/4.TypeScript类/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
5 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
6 | // "lib": [], /* Specify library files to be included in the compilation. */
7 | // "allowJs": true, /* Allow javascript files to be compiled. */
8 | // "checkJs": true, /* Report errors in .js files. */
9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
12 | // "sourceMap": true, /* Generates corresponding '.map' file. */
13 | // "outFile": "./", /* Concatenate and emit output to single file. */
14 | "outDir": "./js", /* Redirect output structure to the directory. */
15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
16 | // "composite": true, /* Enable project compilation */
17 | // "removeComments": true, /* Do not emit comments to output. */
18 | // "noEmit": true, /* Do not emit outputs. */
19 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
20 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
21 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
22 |
23 | /* Strict Type-Checking Options */
24 | "strict": true, /* Enable all strict type-checking options. */
25 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
26 | // "strictNullChecks": true, /* Enable strict null checks. */
27 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
28 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
29 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
30 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
31 |
32 | /* Additional Checks */
33 | // "noUnusedLocals": true, /* Report errors on unused locals. */
34 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
35 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
36 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
37 |
38 | /* Module Resolution Options */
39 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
40 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
41 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
42 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
43 | // "typeRoots": [], /* List of folders to include type definitions from. */
44 | // "types": [], /* Type declaration files to be included in compilation. */
45 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
46 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
47 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
48 |
49 | /* Source Map Options */
50 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
51 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
52 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
53 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
54 |
55 | /* Experimental Options */
56 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
57 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
58 | }
59 | }
--------------------------------------------------------------------------------
/4.TypeScript类/typescript.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * typescript中的类
3 | * 1.ts类的定义
4 | * 2.继承
5 | * 3.ts类里面的修饰符
6 | * 4.静态属性和静态方法
7 | * 5.多态
8 | * 6.抽象类和抽象方法
9 | */
10 |
11 | /**
12 | * 1.ts中类的定义
13 | * 和es6类的定义一样
14 | */
15 | class PersonDefine {
16 | name: string // 属性,前面省略了public关键词
17 | constructor(name:string) { //构造函数
18 | this.name = name
19 | }
20 | run():string { // 原型
21 | return `${this.name}在运动`
22 | }
23 | }
24 | var define = new PersonDefine('类的定义')
25 | console.log(define.run())
26 |
27 | /**
28 | * 2.继承
29 | * 用extends super实现继承
30 | */
31 | class WebExtend extends PersonDefine {
32 | constructor(name:string) {
33 | super(name) // super继承父类的构造函数,并向父类构造函数传参
34 | }
35 | work():string {
36 | return `${this.name}在工作`
37 | }
38 | }
39 |
40 | var extend = new WebExtend('继承')
41 | console.log(extend.run())
42 | console.log(extend.work())
43 |
44 | /**
45 | * 3.ts类里面的修饰符
46 | * 修饰符:typescript里面定义属性的时候给我们提供了三种修饰符
47 | * public:公有修饰符,在当前类里面、子类、类外面都可以访问
48 | * protected:保护类型,在当前类里面、子类里面可以访问,在类外部没法访问
49 | * private :私有修饰符,在当前类里面可以访问,子类、类外部都没法访问
50 | * 属性如果不加修饰符,默认就是公有修饰符
51 | */
52 |
53 |
54 | // 以private为例
55 | class PersonPrivate{
56 | private name:string; /*被private修饰的属性 => 私有属性*/
57 | constructor(name:string){
58 | this.name=name;
59 | }
60 | run():string{
61 | return `${this.name}在运动` // 私有属性只能在当前类里面可以访问
62 | }
63 | }
64 |
65 | class Web extends PersonPrivate{
66 | constructor(name:string){
67 | super(name)
68 | }
69 | work(){
70 | // return `${this.name}在工作` // 报错,子类不能访问父类的私有属性
71 | }
72 | }
73 |
74 | var privateName = new PersonPrivate('private')
75 | console.log(privateName.run())
76 | // console.log(privateName.name) // 报错,外部不能访问类的私有属性
77 |
78 | /**
79 | * 4.静态属性和静态方法
80 | * 为什么要用静态属性和静态方法?jq里面的$.ajax就是用的静态方法
81 | */
82 |
83 | /*
84 | function $(element) {
85 | return new Base(element)
86 | }
87 |
88 | function Base(element) {
89 | this.element = document.getElementById(element)
90 | this.css = function(arr, value) {
91 | this.element.style[arr] = value
92 | }
93 | }
94 | $('box').css('color','red')
95 | $.ajax = function() {} // 想要在$上使用方法怎么办,用静态方法
96 | */
97 |
98 | // ts中实现静态属性和静态方法用static
99 |
100 | class PersonStatic{
101 | /*公有属性*/
102 | public name:string;
103 | constructor(name:string) {
104 | this.name=name;
105 | }
106 | /*实例方法(需要被实例化,所以为实例方法)*/
107 | run(){
108 | return `${this.name}在运动`
109 | }
110 | /*静态属性*/
111 | static sex = '男'
112 | /*静态方法,里面没法直接调用类里面的属性*/
113 | static info(){
114 | // return 'info方法' + this.name // 静态方法不能调用本类的方法和属性,可以调用静态属性
115 | return 'info方法' + PersonStatic.sex
116 | }
117 | }
118 |
119 | console.log('静态方法' + PersonStatic.info())
120 | console.log('静态属性' + PersonStatic.sex)
121 |
122 | /**
123 | * 5.多态
124 | * 父类定义一个方法不去实现,让继承它的子类去实现,每一个子类的该方法有不同的表现
125 | * 多态属于继承
126 | */
127 |
128 | // 比如定义一个父类Animal,里面的eat方法不去实现,让子类Dog和Cat分别实现自己的eat方法
129 | class Animal {
130 | name:string;
131 | constructor(name:string) {
132 | this.name=name;
133 | }
134 | eat(){ // eat方法继承它的子类去实现
135 | }
136 | }
137 | class Dog extends Animal{
138 | constructor(name:string){
139 | super(name)
140 | }
141 | eat(){
142 | return this.name+'吃粮食'
143 | }
144 | }
145 |
146 | class Cat extends Animal{
147 | constructor(name:string){
148 | super(name)
149 | }
150 | eat(){
151 | return this.name+'吃老鼠'
152 | }
153 | }
154 |
155 | /**
156 | * 6.抽象类和抽象方法
157 | * 抽象类:它是提供其他类继承的基类,不能直接被实例化,子类继承可以被实例化
158 | * 定义:用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类(抽象类的子类)中实现。
159 | * abstract修饰的方法(抽象方法)只能放在抽象类里面
160 | * 抽象类和抽象方法用来定义标准(比如定义标准为:抽象类Animal有抽象方法eat,要求它的子类必须包含eat方法)
161 | */
162 |
163 | abstract class AnimalAbst{
164 | public name:string;
165 | constructor(name:string){
166 | this.name=name;
167 | }
168 | abstract eat():any; //抽象方法不包含具体实现并且必须在派生类中实现
169 | run(){
170 | console.log('其他方法可以不实现')
171 | }
172 | }
173 | // var a = new Animal() /*错误的写法,抽象类不能被实例化*/
174 |
175 | class DogAbst extends Animal{
176 | //抽象类的子类必须实现抽象类里面的抽象方法
177 | constructor(name:any){
178 | super(name)
179 | }
180 | eat(){
181 | return this.name + '吃粮食'
182 | }
183 | }
184 |
185 | var d = new DogAbst('小花花');
186 | console.log('抽象类和抽象方法',d.eat());
--------------------------------------------------------------------------------
/4.TypeScript类/typescript中的类.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 | ts中类的定义、继承、类修饰符、静态属性和静态方法、多态、抽象类和抽象方法
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/5.TypeScript接口/README.md:
--------------------------------------------------------------------------------
1 | # TypesSript接口
2 |
3 | **接口定义**:接口是对传入参数进行约束;或者对类里面的属性和方法进行声明和约束,实现这个接口的类必须实现该接口里面属性和方法;typescript中的接口用interface关键字定义。
4 |
5 | 接口作用:接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。
6 |
7 | 内容概述:接口分类:(属性接口、函数类型接口、可索引接口、类类型接口),接口的继承
8 |
9 | ### 1. 接口分类
10 |
11 | ### 1.1 属性接口
12 |
13 | > 对传入对象的约束(也就是对json的约束)
14 |
15 | 在了解接口之前,我们来看看函数传入obj参数
16 |
17 | ```js
18 |
19 | function printLabel(labelInfo: {label:string}){
20 | return labelInfo
21 | }
22 | // printLabel({name:'obj'}); //错误的写法
23 | console.log(printLabel({label: 'obj'}))
24 |
25 | ```
26 |
27 | 和上面类似,由此引入属性接口 => 对方法传入参数进行约束
28 |
29 |
30 | 下面为属性接口的例子,方法printFullName对传入参数FullName(为对象)进行约束
31 |
32 | ```js
33 |
34 | interface FullName{
35 | firstName: string; // 注意;结束
36 | secondName: string;
37 | age?: number // 接口的可选属性用?
38 | }
39 |
40 | function printFullName(name:FullName) {
41 | // 传入对象必须包含firstName和secondName,可传可不传age
42 | return name
43 | }
44 | var obj = {
45 | firstName:'小',
46 | secondName:'明',
47 | age: 20
48 | }
49 | console.log(printFullName(obj))
50 |
51 | ```
52 |
53 | **属性接口应用:原生js封装ajax**
54 |
55 | ```js
56 |
57 | interface Config{
58 | type: string;
59 | url: string;
60 | data?: string;
61 | dataType: string;
62 | }
63 | function ajax(config: Config) {
64 | var xhr = new XMLHttpRequest
65 | xhr.open(config.type, config.url, true)
66 | xhr.send(config.data)
67 | xhr.onreadystatechange = function() {
68 | if(xhr.readyState == 4 && xhr.status == 200) {
69 | if(config.dataType == 'json'){
70 | console.log(JSON.parse(xhr.responseText))
71 | }else{
72 | console.log(xhr.responseText)
73 | }
74 | }
75 | }
76 | }
77 |
78 | ajax({
79 | type: 'get',
80 | data: 'name=xiaoming',
81 | url: 'http://a.itying.com/api/productlist',
82 | dataType: 'json'
83 | })
84 |
85 | ```
86 |
87 | ### 1.2 函数类型接口
88 |
89 | > 对方法传入的参数以及返回值进行约束
90 |
91 | ```js
92 |
93 | interface encrypt{
94 | (key: string, value: string): string; // 传入的参数和返回值的类型
95 | }
96 |
97 | var md5:encrypt = function(key:string, value:string):string{
98 | // encrypt对加密方法md5进行约束,同时md5方法的参数和返回值类型和encrypt要保持一致
99 | return key + value
100 | }
101 |
102 | console.log(md5('name', '小明'))
103 |
104 | ```
105 |
106 | ### 1.3 可索引接口
107 |
108 | > 对索引和传入参数的约束(一般用于对数组、对象的约束)
109 |
110 | ts中定义数组:
111 |
112 | ```js
113 |
114 | var arr1:number[] = [1,2]
115 | var arr2:Array = ['1', '2']
116 |
117 | ```
118 |
119 | 现在用接口来实现:
120 |
121 | ```js
122 |
123 | // 对数组的的约束
124 | interface UserArr{
125 | // 索引为number,参数为string
126 | [index:number]: string
127 | }
128 | var userarr:UserArr = ['a', 'b']
129 | console.log(userarr)
130 |
131 | ```
132 |
133 | ```js
134 |
135 | // 对象的约束
136 | interface UserObj{
137 | // 索引为string,参数为string
138 | [index:string]: string
139 | }
140 | var userobj:UserObj = { name: '小明', sex: '男' }
141 | console.log(userobj)
142 |
143 | ```
144 |
145 | ### 1.4 类类型接口
146 |
147 | > 对类的约束,和抽象类抽象有点相似
148 |
149 | ```js
150 |
151 | interface Animal{
152 | // 对类里面的属性和方法进行约束
153 | name:string;
154 | eat(str:string):void;
155 | }
156 | // 类实现接口要用implements关键字,必须实现接口里面声明的方法和属性
157 | class Cat implements Animal{
158 | name:string;
159 | constructor(name:string){
160 | this.name = name
161 | }
162 | eat(food:string){
163 | console.log(this.name + '吃' + food)
164 | }
165 | }
166 | var cat = new Cat('小花')
167 | cat.eat('老鼠')
168 |
169 | ```
170 |
171 | ### 2. 接口的继承
172 |
173 | > 和类的继承一样,用extends实现接口继承
174 |
175 | 下面同时实现类的继承和接口的继承
176 |
177 | ```js
178 |
179 | interface Animal {
180 | eat(): void;
181 | }
182 | // 继承Animal接口,则实现Person接口的类必须也实现Animal接口里面的方法
183 | interface Person extends Animal {
184 | work(): void;
185 | }
186 |
187 | class Programmer {
188 | public name: string;
189 | constructor(name: string) {
190 | this.name = name;
191 | }
192 | coding(code: string) {
193 | console.log(this.name + code)
194 | }
195 | }
196 |
197 | // 继承类并且实现接口
198 | class Web extends Programmer implements Person {
199 | constructor(name: string) {
200 | super(name)
201 | }
202 | eat() {
203 | console.log(this.name + '吃')
204 | }
205 | work() {
206 | console.log(this.name + '工作');
207 | }
208 | }
209 |
210 | var w = new Web('小李');
211 | w.eat();
212 | w.coding('写ts代码');
213 |
214 | ```
215 |
216 | 例子中的[源代码](./index.ts)
--------------------------------------------------------------------------------
/5.TypeScript接口/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * typescript接口
3 | * 接口定义:接口是对传入参数进行约束;或者对类里面的属性和方法进行声明和约束,实现这个接口的类必须实现该接口里面属性和方法;typescript中的接口用interface关键字定义。
4 | * 接口作用:接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。
5 | typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。
6 | * 内容概述:接口分类:属性接口、函数类型接口、可索引接口、类类型接口,接口的继承
7 | */
8 |
9 | /**
10 | * 1.1 属性接口
11 | * 对传入对象的约束(也就是对json的约束)
12 | */
13 |
14 | // 在了解接口之前,我们来看看函数传入obj参数
15 | function printLabel(labelInfo: {label:string}){
16 | return labelInfo
17 | }
18 | // printLabel({name:'obj'}); //错误的写法
19 | console.log(printLabel({label: 'obj'}))
20 |
21 |
22 | // 由此引入属性接口 => 对方法传入参数进行约束
23 | // 下面为属性接口的例子,方法printFullName对传入参数FullName(为对象)进行约束
24 | interface FullName{
25 | firstName: string; // 注意;结束
26 | secondName: string;
27 | age?: number // 接口的可选属性用?
28 | }
29 |
30 | function printFullName(name:FullName) {
31 | // 传入对象必须包含firstName和secondName,可传可不传age
32 | return name
33 | }
34 | var obj = {
35 | firstName:'小',
36 | secondName:'明',
37 | age: 20
38 | }
39 | console.log(printFullName(obj))
40 |
41 | // 属性接口应用:原生js封装ajax
42 | interface Config{
43 | type: string;
44 | url: string;
45 | data?: string;
46 | dataType: string;
47 | }
48 | function ajax(config: Config) {
49 | var xhr = new XMLHttpRequest
50 | xhr.open(config.type, config.url, true)
51 | xhr.send(config.data)
52 | xhr.onreadystatechange = function() {
53 | if(xhr.readyState == 4 && xhr.status == 200) {
54 | if(config.dataType == 'json'){
55 | console.log(JSON.parse(xhr.responseText))
56 | }else{
57 | console.log(xhr.responseText)
58 | }
59 | }
60 | }
61 | }
62 |
63 | ajax({
64 | type: 'get',
65 | data: 'name=xiaoming',
66 | url: 'http://a.itying.com/api/productlist',
67 | dataType: 'json'
68 | })
69 |
70 | /**
71 | * 1.2 函数类型接口
72 | * 对方法传入的参数以及返回值进行约束
73 | */
74 | interface encrypt{
75 | (key: string, value: string): string; // 传入的参数和返回值的类型
76 | }
77 |
78 | var md5:encrypt = function(key:string, value:string):string{
79 | // encrypt对加密方法md5进行约束,同时md5方法的参数和返回值类型和encrypt要保持一致
80 | return key + value
81 | }
82 |
83 | console.log(md5('name', '小明'))
84 |
85 | /**
86 | * 1.3 可索引接口
87 | * 对索引和传入参数的约束(一般用于对数组、对象的约束)
88 | */
89 |
90 | // ts中定义数组
91 | var arr1:number[] = [1,2]
92 | var arr2:Array = ['1', '2']
93 |
94 | // 对数组的的约束
95 | interface UserArr{
96 | // 索引为number,参数为string
97 | [index:number]: string
98 | }
99 | var userarr:UserArr = ['a', 'b']
100 | console.log(userarr)
101 |
102 | // 对象的约束
103 | interface UserObj{
104 | // 索引为string,参数为string
105 | [index:string]: string
106 | }
107 | var userobj:UserObj = { name: '小明', sex: '男' }
108 | console.log(userobj)
109 |
110 | /**
111 | * 1.4 类类型接口
112 | * 对类的约束,和抽象类抽象有点相似
113 | */
114 | interface AnimalClass{
115 | // 对类里面的属性和方法进行约束
116 | name:string;
117 | eat(str:string):void;
118 | }
119 | // 类实现接口要用implements关键字,必须实现接口里面声明的方法和属性
120 | class Cat implements AnimalClass{
121 | name:string;
122 | constructor(name:string){
123 | this.name = name
124 | }
125 | eat(food:string){
126 | console.log(this.name + '吃' + food)
127 | }
128 | }
129 | var cat = new Cat('小花')
130 | cat.eat('老鼠')
131 |
132 | /**
133 | * 2. 接口的继承
134 | * 和类的继承一样,用extends实现接口继承
135 | */
136 |
137 | interface Animal {
138 | eat(): void;
139 | }
140 | // 继承Animal接口,则实现Person接口的类必须也实现Animal接口里面的方法
141 | interface Person extends Animal {
142 | work(): void;
143 | }
144 |
145 | class Programmer {
146 | public name: string;
147 | constructor(name: string) {
148 | this.name = name;
149 | }
150 | coding(code: string) {
151 | console.log(this.name + code)
152 | }
153 | }
154 |
155 | // 继承类并且实现接口
156 | class Web extends Programmer implements Person {
157 | constructor(name: string) {
158 | super(name)
159 | }
160 | eat() {
161 | console.log(this.name + '吃')
162 | }
163 | work() {
164 | console.log(this.name + '工作');
165 | }
166 | }
167 |
168 | var w = new Web('小李');
169 | w.eat();
170 | w.coding('写ts代码');
--------------------------------------------------------------------------------
/5.TypeScript接口/js/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | /**
3 | * typescript接口
4 | * 接口定义:接口是对传入参数进行约束;或者对类里面的属性和方法进行声明和约束,实现这个接口的类必须实现该接口里面属性和方法;typescript中的接口用interface关键字定义。
5 | * 接口作用:接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。
6 | typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。
7 | * 内容概述:接口分类:属性接口、函数类型接口、可索引接口、类类型接口,接口的继承
8 | */
9 | var __extends = (this && this.__extends) || (function () {
10 | var extendStatics = function (d, b) {
11 | extendStatics = Object.setPrototypeOf ||
12 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
13 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
14 | return extendStatics(d, b);
15 | }
16 | return function (d, b) {
17 | extendStatics(d, b);
18 | function __() { this.constructor = d; }
19 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
20 | };
21 | })();
22 | /**
23 | * 1.1 属性接口
24 | * 对传入对象的约束(也就是对json的约束)
25 | */
26 | // 在了解接口之前,我们来看看函数传入obj参数
27 | function printLabel(labelInfo) {
28 | return labelInfo;
29 | }
30 | // printLabel({name:'obj'}); //错误的写法
31 | console.log(printLabel({ label: 'obj' }));
32 | function printFullName(name) {
33 | // 传入对象必须包含firstName和secondName,可传可不传age
34 | return name;
35 | }
36 | var obj = {
37 | firstName: '小',
38 | secondName: '明',
39 | age: 20
40 | };
41 | console.log(printFullName(obj));
42 | function ajax(config) {
43 | var xhr = new XMLHttpRequest;
44 | xhr.open(config.type, config.url, true);
45 | xhr.send(config.data);
46 | xhr.onreadystatechange = function () {
47 | if (xhr.readyState == 4 && xhr.status == 200) {
48 | if (config.dataType == 'json') {
49 | console.log(JSON.parse(xhr.responseText));
50 | }
51 | else {
52 | console.log(xhr.responseText);
53 | }
54 | }
55 | };
56 | }
57 | ajax({
58 | type: 'get',
59 | data: 'name=xiaoming',
60 | url: 'http://a.itying.com/api/productlist',
61 | dataType: 'json'
62 | });
63 | var md5 = function (key, value) {
64 | // encrypt对加密方法md5进行约束,同时md5方法的参数和返回值类型和encrypt要保持一致
65 | return key + value;
66 | };
67 | console.log(md5('name', '小明'));
68 | /**
69 | * 1.3 可索引接口
70 | * 对索引和传入参数的约束(一般用于对数组、对象的约束)
71 | */
72 | // ts中定义数组
73 | var arr1 = [1, 2];
74 | var arr2 = ['1', '2'];
75 | var userarr = ['a', 'b'];
76 | console.log(userarr);
77 | var userobj = { name: '小明', sex: '男' };
78 | console.log(userobj);
79 | // 类实现接口要用implements关键字,必须实现接口里面声明的方法和属性
80 | var Cat = /** @class */ (function () {
81 | function Cat(name) {
82 | this.name = name;
83 | }
84 | Cat.prototype.eat = function (food) {
85 | console.log(this.name + '吃' + food);
86 | };
87 | return Cat;
88 | }());
89 | var cat = new Cat('小花');
90 | cat.eat('老鼠');
91 | var Programmer = /** @class */ (function () {
92 | function Programmer(name) {
93 | this.name = name;
94 | }
95 | Programmer.prototype.coding = function (code) {
96 | console.log(this.name + code);
97 | };
98 | return Programmer;
99 | }());
100 | // 继承类并且实现接口
101 | var Web = /** @class */ (function (_super) {
102 | __extends(Web, _super);
103 | function Web(name) {
104 | return _super.call(this, name) || this;
105 | }
106 | Web.prototype.eat = function () {
107 | console.log(this.name + '吃');
108 | };
109 | Web.prototype.work = function () {
110 | console.log(this.name + '工作');
111 | };
112 | return Web;
113 | }(Programmer));
114 | var w = new Web('小李');
115 | w.eat();
116 | w.coding('写ts代码');
117 |
--------------------------------------------------------------------------------
/5.TypeScript接口/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
5 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
6 | // "lib": [], /* Specify library files to be included in the compilation. */
7 | // "allowJs": true, /* Allow javascript files to be compiled. */
8 | // "checkJs": true, /* Report errors in .js files. */
9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
12 | // "sourceMap": true, /* Generates corresponding '.map' file. */
13 | // "outFile": "./", /* Concatenate and emit output to single file. */
14 | "outDir": "./js", /* Redirect output structure to the directory. */
15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
16 | // "composite": true, /* Enable project compilation */
17 | // "removeComments": true, /* Do not emit comments to output. */
18 | // "noEmit": true, /* Do not emit outputs. */
19 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
20 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
21 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
22 |
23 | /* Strict Type-Checking Options */
24 | "strict": true, /* Enable all strict type-checking options. */
25 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
26 | // "strictNullChecks": true, /* Enable strict null checks. */
27 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
28 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
29 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
30 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
31 |
32 | /* Additional Checks */
33 | // "noUnusedLocals": true, /* Report errors on unused locals. */
34 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
35 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
36 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
37 |
38 | /* Module Resolution Options */
39 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
40 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
41 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
42 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
43 | // "typeRoots": [], /* List of folders to include type definitions from. */
44 | // "types": [], /* Type declaration files to be included in compilation. */
45 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
46 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
47 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
48 |
49 | /* Source Map Options */
50 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
51 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
52 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
53 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
54 |
55 | /* Experimental Options */
56 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
57 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
58 | }
59 | }
--------------------------------------------------------------------------------
/5.TypeScript接口/typescript接口.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 | 内容概述:接口分类:属性接口、函数类型接口、可索引接口、类类型接口,接口的继承
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/6.TypeScript泛型/README.md:
--------------------------------------------------------------------------------
1 | # TypesSript泛型
2 |
3 | **泛型**:很多时候,类型是写死的,不利于复用,泛型可以简单理解为给类型这种值设置变量,解决类、接口、方法的复用性,以及对不特定数据类型的支持。
4 |
5 | * 语法: <类型变量名>,一般系统使用单字母大写,比如 T、S...
6 |
7 | 内容概述:泛型函数、泛型类、泛型接口
8 |
9 | ## 泛型函数
10 |
11 | > 传入的参数类型和返回的参数类型可以指定
12 |
13 | 我们来看看函数用ts数据类型,想要同时返回string类型和number类型
14 |
15 | ```js
16 |
17 | function getData1(value:string):string{
18 | return value;
19 | }
20 | function getData2(value:number):number{
21 | return value;
22 | }
23 |
24 | ```
25 |
26 | 这样要写不同的函数,不能按照需求返回不同类型数据,造成代码冗余 => 由此引入泛型
27 |
28 |
29 | > 表示泛型,调用的时候指定T的数据类型
30 |
31 | ```js
32 |
33 | function dataT(value:T):T{
34 | // 传入参数为T 返回值为T
35 | return value
36 | }
37 | dataT(1) // 调用指定泛型为number类型,则传入参数也必须为number类型
38 | dataT('string')
39 |
40 | function dataAny(value:T):any{
41 | return '传入参数为T,任意类型返回值';
42 | }
43 | dataAny(123); // 参数必须是number
44 | dataAny('这是一个泛型');
45 |
46 | ```
47 |
48 | ## 泛型类
49 |
50 | > 泛型类使用(<>)括起泛型类型,跟在类名后面。
51 |
52 | 有个最小堆算法,需要同时支持返回数字和字符串两种类型
53 |
54 | 使用泛型之前:只能在类的类部指定数据类型,实现需求还要写一套string类型的类
55 |
56 | ```js
57 |
58 | class MinClass{
59 | public list:number[]=[];
60 | add(num:number){
61 | this.list.push(num)
62 | }
63 | min():number{
64 | var minNum=this.list[0];
65 | for(var i=0;ithis.list[i]){
67 | minNum=this.list[i];
68 | }
69 | }
70 | return minNum;
71 | }
72 | }
73 |
74 | var m=new MinClass();
75 | m.add(1);
76 | m.add(2);
77 | alert(m.min());
78 |
79 | ```
80 |
81 | 使用泛型之后:只用一套类来实现
82 |
83 | ```js
84 |
85 | class MinClassT{
86 | public list:T[]=[];
87 | add(value:T):void{
88 | this.list.push(value);
89 | }
90 | min():T{
91 | var minNum=this.list[0];
92 | for(var i=0;ithis.list[i]){
94 | minNum=this.list[i];
95 | }
96 | }
97 | return minNum;
98 | }
99 | }
100 | var m1=new MinClassT(); /*实例化类 并且指定了类的T代表的类型是number*/
101 | m.add(1);
102 | m.add(2);
103 | alert(m1.min())
104 |
105 | var m2=new MinClassT(); /*实例化类 并且指定了类的T代表的类型是string*/
106 | m2.add('c');
107 | m2.add('a');
108 | alert(m2.min())
109 |
110 | ```
111 |
112 | ## 泛型接口
113 |
114 | 有一个函数类型接口
115 |
116 | ```js
117 |
118 | interface ConfigFn{
119 | (value:string):string;
120 | }
121 | var setData:ConfigFn = function(value:string):string{
122 | return value
123 | }
124 | setData('name');
125 | // setData(20); // 错误
126 |
127 | ```
128 | setData(20);写法错误,想要传入number类型的参数又要写一个函数类型接口 => 用泛型接口
129 |
130 | 泛型接口有两种写法:
131 |
132 | ```js
133 |
134 | // 泛型接口定义方式一
135 | interface ConfigFnOne{
136 | (value:T):T;
137 | }
138 | var setDataOne:ConfigFnOne = function(value:T):T{
139 | return value
140 | }
141 | // 既可以传入string也可以传入number类型参数
142 | setDataOne('name');
143 | setDataOne(20);
144 |
145 | ```
146 |
147 | ```js
148 |
149 | // 泛型接口定义方式二
150 | interface ConfigFnTwo{
151 | (value:T):T;
152 | }
153 | function setDataTwo(value:T):T{
154 | return value
155 | }
156 | var setDataTwoFn:ConfigFnTwo = setDataTwo
157 | setDataTwoFn('name');
158 |
159 | ```
160 |
161 | 例子中的[源代码](./index.ts)
--------------------------------------------------------------------------------
/6.TypeScript泛型/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * typescript泛型
3 | * 泛型定义:泛型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持(类型校验)。ts中用T表示泛型
4 | * 软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
5 | 在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
6 | * 内容概述:函数的泛型、类的泛型、泛型接口
7 | */
8 |
9 | // 泛型公式: 表示泛型,调用的时候指定T的数据类型
10 |
11 | /**
12 | * 1. 函数的泛型
13 | * 传入的参数类型和返回的参数类型可以指定
14 | */
15 |
16 | // 我们来看看函数用ts数据类型,想要同时返回string类型和number类型
17 | function getData1(value:string):string{
18 | return value;
19 | }
20 | function getData2(value:number):number{
21 | return value;
22 | }
23 | // 这样要写不同的函数,不能按照需求返回不同类型数据,造成代码冗余 => 由此引入泛型
24 |
25 | // 表示泛型,调用的时候指定T的数据类型
26 | function dataT(value:T):T{
27 | // 传入参数为T 返回值为T
28 | return value
29 | }
30 | dataT(1) // 调用指定泛型为number类型,则传入参数也必须为number类型
31 | dataT('string')
32 |
33 | function dataAny(value:T):any{
34 | return '传入参数为T,任意类型返回值';
35 | }
36 | dataAny(123); // 参数必须是number
37 | dataAny('这是一个泛型');
38 |
39 |
40 | /**
41 | * 2. 类的泛型
42 | * 也是用来实现类的泛型,new的时候指定T的数据类型
43 | */
44 |
45 | // 有个最小堆算法,需要同时支持返回数字和字符串两种类型
46 |
47 | // 使用泛型之前:只能在类的类部指定数据类型,实现需求还要写一套string类型的类
48 | class MinClass{
49 | public list:number[]=[];
50 | add(num:number){
51 | this.list.push(num)
52 | }
53 | min():number{
54 | var minNum=this.list[0];
55 | for(var i=0;ithis.list[i]){
57 | minNum=this.list[i];
58 | }
59 | }
60 | return minNum;
61 | }
62 | }
63 |
64 | var m=new MinClass();
65 | m.add(1);
66 | m.add(2);
67 | alert(m.min());
68 |
69 | // 使用泛型之后:只用一套类来实现
70 | class MinClassT{
71 | public list:T[]=[];
72 | add(value:T):void{
73 | this.list.push(value);
74 | }
75 | min():T{
76 | var minNum=this.list[0];
77 | for(var i=0;ithis.list[i]){
79 | minNum=this.list[i];
80 | }
81 | }
82 | return minNum;
83 | }
84 | }
85 | var m1=new MinClassT(); /*实例化类 并且指定了类的T代表的类型是number*/
86 | m.add(1);
87 | m.add(2);
88 | alert(m1.min())
89 |
90 | var m2=new MinClassT(); /*实例化类 并且指定了类的T代表的类型是string*/
91 | m2.add('c');
92 | m2.add('a');
93 | alert(m2.min())
94 |
95 | /**
96 | * 3. 泛型接口
97 | */
98 |
99 | // 有一个函数类型接口
100 | interface ConfigFn{
101 | (value:string):string;
102 | }
103 | var setData:ConfigFn = function(value:string):string{
104 | return value
105 | }
106 | setData('name');
107 | // setData(20); // 想要传入number类型的参数又要写一个函数类型接口 => 用泛型接口
108 |
109 |
110 | // 泛型接口定义方式一
111 | interface ConfigFnOne{
112 | (value:T):T;
113 | }
114 | var setDataOne:ConfigFnOne = function(value:T):T{
115 | return value
116 | }
117 | // 既可以传入string也可以传入number类型参数
118 | setDataOne('name');
119 | setDataOne(20);
120 |
121 |
122 | // 泛型接口定义方式二
123 | interface ConfigFnTwo{
124 | (value:T):T;
125 | }
126 | function setDataTwo(value:T):T{
127 | return value
128 | }
129 | var setDataTwoFn:ConfigFnTwo = setDataTwo
130 | setDataTwoFn('name');
--------------------------------------------------------------------------------
/6.TypeScript泛型/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
5 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
6 | // "lib": [], /* Specify library files to be included in the compilation. */
7 | // "allowJs": true, /* Allow javascript files to be compiled. */
8 | // "checkJs": true, /* Report errors in .js files. */
9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
12 | // "sourceMap": true, /* Generates corresponding '.map' file. */
13 | // "outFile": "./", /* Concatenate and emit output to single file. */
14 | "outDir": "./js", /* Redirect output structure to the directory. */
15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
16 | // "composite": true, /* Enable project compilation */
17 | // "removeComments": true, /* Do not emit comments to output. */
18 | // "noEmit": true, /* Do not emit outputs. */
19 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
20 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
21 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
22 |
23 | /* Strict Type-Checking Options */
24 | "strict": true, /* Enable all strict type-checking options. */
25 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
26 | // "strictNullChecks": true, /* Enable strict null checks. */
27 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
28 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
29 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
30 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
31 |
32 | /* Additional Checks */
33 | // "noUnusedLocals": true, /* Report errors on unused locals. */
34 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
35 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
36 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
37 |
38 | /* Module Resolution Options */
39 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
40 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
41 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
42 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
43 | // "typeRoots": [], /* List of folders to include type definitions from. */
44 | // "types": [], /* Type declaration files to be included in compilation. */
45 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
46 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
47 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
48 |
49 | /* Source Map Options */
50 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
51 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
52 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
53 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
54 |
55 | /* Experimental Options */
56 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
57 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
58 | }
59 | }
--------------------------------------------------------------------------------
/6.TypeScript泛型/typescript泛型.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 | 内容概述:函数的泛型、类的泛型、泛型接口
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/7.TypeScript类型推论/README.md:
--------------------------------------------------------------------------------
1 | # TypesSript类型推论
2 |
3 | 有的时候不一定需要强制使用类型声明,在有些没有明确指出类型的地方,ts类型推论会帮助提供类型。
4 |
5 | 内容概述:变量初始化类型推论、上下文类型推论。
6 |
7 | ## 变量初始化
8 |
9 | ts会根据变量初始化的时候赋予的值进行类型推断。
10 |
11 | ```ts
12 | let a = '类型推论';
13 | // a = true; // Type 'true' is not assignable to type 'string'
14 | ```
15 | 上面代码中,a初始化没有指定类型,ts会推论出a的类型为string,当a = true重新赋值的时候类型不匹配会报相应错误,vscode编译器会提示错误。
16 |
17 | ## 上下文推断
18 |
19 | ts也会根据上下文进行类型的推断,比如在事件函数中,函数的第一个参数会根据当前绑定的事件类型推断处理事件对象。
20 |
21 | ```ts
22 | document.onkeydown = function(e) {
23 | // console.log(e.button); //<- Error Property 'button' does not exist on type 'KeyboardEvent'
24 | };
25 | ```
26 | 这个例子会得到一个类型错误,ts类型检查器根据当前绑定的事件类onkeydown自动推导e的类型为KeyboardEvent,vscode编译器里鼠标放上去就有e推导出来的类型(e:KeyboardEvent)
27 |
28 | 例子中的[源代码](./index.ts)
--------------------------------------------------------------------------------
/7.TypeScript类型推论/index.ts:
--------------------------------------------------------------------------------
1 | // 变量初始化类型推论
2 | {
3 | let a = '类型推论';
4 | // a = true; // Type 'true' is not assignable to type 'string'
5 | }
6 |
7 | // 上下文推断
8 | {
9 | // ts类型检查器根据当前绑定的事件类onkeydown自动推导e的类型为KeyboardEvent,vscode编译器里鼠标放上去就有e推导出来的类型(e:KeyboardEvent)
10 | document.onkeydown = function(e) {
11 | // console.log(e.button); //<- Error Property 'button' does not exist on type 'KeyboardEvent'
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/7.TypeScript类型推论/js/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | // 变量初始化类型推论
3 | {
4 | var a = '类型推论';
5 | // a = true; // Type 'true' is not assignable to type 'string'
6 | }
7 | // 上下文推断
8 | {
9 | // ts类型检查器根据当前绑定的事件类onkeydown自动推导e的类型为KeyboardEvent,vscode编译器里鼠标放上去就有e推导出来的类型(e:KeyboardEvent)
10 | document.onkeydown = function (e) {
11 | // console.log(e.button); //<- Error Property 'button' does not exist on type 'KeyboardEvent'
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/7.TypeScript类型推论/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
5 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
6 | // "lib": [], /* Specify library files to be included in the compilation. */
7 | // "allowJs": true, /* Allow javascript files to be compiled. */
8 | // "checkJs": true, /* Report errors in .js files. */
9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
12 | // "sourceMap": true, /* Generates corresponding '.map' file. */
13 | // "outFile": "./", /* Concatenate and emit output to single file. */
14 | "outDir": "./js", /* Redirect output structure to the directory. */
15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
16 | // "composite": true, /* Enable project compilation */
17 | // "removeComments": true, /* Do not emit comments to output. */
18 | // "noEmit": true, /* Do not emit outputs. */
19 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
20 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
21 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
22 |
23 | /* Strict Type-Checking Options */
24 | "strict": true, /* Enable all strict type-checking options. */
25 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
26 | // "strictNullChecks": true, /* Enable strict null checks. */
27 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
28 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
29 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
30 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
31 |
32 | /* Additional Checks */
33 | // "noUnusedLocals": true, /* Report errors on unused locals. */
34 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
35 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
36 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
37 |
38 | /* Module Resolution Options */
39 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
40 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
41 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
42 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
43 | // "typeRoots": [], /* List of folders to include type definitions from. */
44 | // "types": [], /* Type declaration files to be included in compilation. */
45 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
46 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
47 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
48 |
49 | /* Source Map Options */
50 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
51 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
52 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
53 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
54 |
55 | /* Experimental Options */
56 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
57 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
58 | }
59 | }
--------------------------------------------------------------------------------
/8.TypeScript高级类型/README.md:
--------------------------------------------------------------------------------
1 | # TypesSript高级类型
2 |
3 | # 交叉类型(Intersection Types)
4 |
5 | 交叉类型是将多个类型合并为一个类型。
6 | 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。
7 | 例如,`Person & Serializable & Loggable`同时是`Person`*和*`Serializable`*和*`Loggable`。
8 | 就是说这个类型的对象同时拥有了这三种类型的成员。
9 |
10 | 我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。
11 | (在JavaScript里发生这种情况的场合很多!)
12 | 下面是如何创建混入的一个简单例子("target": "es5"):
13 |
14 | ```ts
15 | function extend(first: T, second: U): T & U {
16 | let result = {};
17 | for (let id in first) {
18 | (result)[id] = (first)[id];
19 | }
20 | for (let id in second) {
21 | if (!result.hasOwnProperty(id)) {
22 | (result)[id] = (second)[id];
23 | }
24 | }
25 | return result;
26 | }
27 |
28 | class Person {
29 | constructor(public name: string) { }
30 | }
31 | interface Loggable {
32 | log(): void;
33 | }
34 | class ConsoleLogger implements Loggable {
35 | log() {
36 | // ...
37 | }
38 | }
39 | var jim = extend(new Person("Jim"), new ConsoleLogger());
40 | var n = jim.name;
41 | jim.log();
42 | ```
43 |
44 | # 联合类型(Union Types)
45 |
46 | 联合类型与交叉类型很有关联,但是使用上却完全不同。
47 | 偶尔你会遇到这种情况,一个代码库希望传入`number`或`string`类型的参数。
48 | 例如下面的函数:
49 |
50 | ```ts
51 | /**
52 | * Takes a string and adds "padding" to the left.
53 | * If 'padding' is a string, then 'padding' is appended to the left side.
54 | * If 'padding' is a number, then that number of spaces is added to the left side.
55 | */
56 | function padLeft(value: string, padding: any) {
57 | if (typeof padding === "number") {
58 | return Array(padding + 1).join(" ") + value;
59 | }
60 | if (typeof padding === "string") {
61 | return padding + value;
62 | }
63 | throw new Error(`Expected string or number, got '${padding}'.`);
64 | }
65 |
66 | padLeft("Hello world", 4); // returns " Hello world"
67 | ```
68 |
69 | `padLeft`存在一个问题,`padding`参数的类型指定成了`any`。
70 | 这就是说我们可以传入一个既不是`number`也不是`string`类型的参数,但是TypeScript却不报错。
71 |
72 | ```ts
73 | let indentedString = padLeft("Hello world", true); // 编译阶段通过,运行时报错
74 | ```
75 |
76 | 在传统的面向对象语言里,我们可能会将这两种类型抽象成有层级的类型。
77 | 这么做显然是非常清晰的,但同时也存在了过度设计。
78 | `padLeft`原始版本的好处之一是允许我们传入原始类型。
79 | 这样做的话使用起来既简单又方便。
80 | 如果我们就是想使用已经存在的函数的话,这种新的方式就不适用了。
81 |
82 | 代替`any`, 我们可以使用*联合类型*做为`padding`的参数:
83 |
84 | ```ts
85 | /**
86 | * Takes a string and adds "padding" to the left.
87 | * If 'padding' is a string, then 'padding' is appended to the left side.
88 | * If 'padding' is a number, then that number of spaces is added to the left side.
89 | */
90 | function padLeft(value: string, padding: string | number) {
91 | // ...
92 | }
93 |
94 | let indentedString = padLeft("Hello world", true); // errors during compilation
95 | ```
96 |
97 | 联合类型表示一个值可以是几种类型之一。
98 | 我们用竖线(`|`)分隔每个类型,所以`number | string | boolean`表示一个值可以是`number`,`string`,或`boolean`。
99 |
100 | 如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。
101 |
102 | ```ts
103 | interface Bird {
104 | fly();
105 | layEggs();
106 | }
107 |
108 | interface Fish {
109 | swim();
110 | layEggs();
111 | }
112 |
113 | function getSmallPet(): Fish | Bird {
114 | // ...
115 | }
116 |
117 | let pet = getSmallPet();
118 | pet.layEggs(); // okay
119 | pet.swim(); // errors
120 | ```
121 |
122 | 这里的联合类型可能有点复杂,但是你很容易就习惯了。
123 | 如果一个值的类型是`A | B`,我们能够*确定*的是它包含了`A`*和*`B`中共有的成员。
124 | 这个例子里,`Bird`具有一个`fly`成员。
125 | 我们不能确定一个`Bird | Fish`类型的变量是否有`fly`方法。
126 | 如果变量在运行时是`Fish`类型,那么调用`pet.fly()`就出错了。
127 |
128 | # 类型守卫与类型区分(Type Guards and Differentiating Types)
129 |
130 | 联合类型适合于那些值可以为不同类型的情况。
131 | 但当我们想确切地了解是否为`Fish`时怎么办?
132 | JavaScript里常用来区分2个可能值的方法是检查成员是否存在。
133 | 如之前提及的,我们只能访问联合类型中共同拥有的成员。
134 |
135 | ```ts
136 | let pet = getSmallPet();
137 |
138 | // 每一个成员访问都会报错
139 | if (pet.swim) {
140 | pet.swim();
141 | }
142 | else if (pet.fly) {
143 | pet.fly();
144 | }
145 | ```
146 |
147 | 为了让这段代码工作,我们要使用类型断言:
148 |
149 | ```ts
150 | let pet = getSmallPet();
151 |
152 | if ((pet).swim) {
153 | (pet).swim();
154 | }
155 | else {
156 | (pet).fly();
157 | }
158 | ```
159 |
160 | ## 用户自定义的类型守卫
161 |
162 | 这里可以注意到我们不得不多次使用类型断言。
163 | 假若我们一旦检查过类型,就能在之后的每个分支里清楚地知道`pet`的类型的话就好了。
164 |
165 | TypeScript里的*类型守卫*机制让它成为了现实。
166 | 类型守卫就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。
167 | 要定义一个类型守卫,我们只要简单地定义一个函数,它的返回值是一个*类型谓词*:
168 |
169 | ```ts
170 | function isFish(pet: Fish | Bird): pet is Fish {
171 | return (pet).swim !== undefined;
172 | }
173 | ```
174 |
175 | 在这个例子里,`pet is Fish`就是类型谓词。
176 | 谓词为`parameterName is Type`这种形式,`parameterName`必须是来自于当前函数签名里的一个参数名。
177 |
178 | 每当使用一些变量调用`isFish`时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。
179 |
180 | ```ts
181 | // 'swim' 和 'fly' 调用都没有问题了
182 |
183 | if (isFish(pet)) {
184 | pet.swim();
185 | }
186 | else {
187 | pet.fly();
188 | }
189 | ```
190 |
191 | 注意TypeScript不仅知道在`if`分支里`pet`是`Fish`类型;
192 | 它还清楚在`else`分支里,一定*不是*`Fish`类型,一定是`Bird`类型。
193 |
194 | ## `typeof`类型守卫
195 |
196 | 现在我们回过头来看看怎么使用联合类型书写`padLeft`代码。
197 | 我们可以像下面这样利用类型断言来写:
198 |
199 | ```ts
200 | function isNumber(x: any): x is number {
201 | return typeof x === "number";
202 | }
203 |
204 | function isString(x: any): x is string {
205 | return typeof x === "string";
206 | }
207 |
208 | function padLeft(value: string, padding: string | number) {
209 | if (isNumber(padding)) {
210 | return Array(padding + 1).join(" ") + value;
211 | }
212 | if (isString(padding)) {
213 | return padding + value;
214 | }
215 | throw new Error(`Expected string or number, got '${padding}'.`);
216 | }
217 | ```
218 |
219 | 然而,必须要定义一个函数来判断类型是否是原始类型,这太痛苦了。
220 | 幸运的是,现在我们不必将`typeof x === "number"`抽象成一个函数,因为TypeScript可以将它识别为一个类型守卫。
221 | 也就是说我们可以直接在代码里检查类型了。
222 |
223 | ```ts
224 | function padLeft(value: string, padding: string | number) {
225 | if (typeof padding === "number") {
226 | return Array(padding + 1).join(" ") + value;
227 | }
228 | if (typeof padding === "string") {
229 | return padding + value;
230 | }
231 | throw new Error(`Expected string or number, got '${padding}'.`);
232 | }
233 | ```
234 |
235 | 这些*`typeof`类型守卫*只有两种形式能被识别:`typeof v === "typename"`和`typeof v !== "typename"`,`"typename"`必须是`"number"`,`"string"`,`"boolean"`或`"symbol"`。
236 | 但是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型守卫。
237 |
238 | ## `instanceof`类型守卫
239 |
240 | 如果你已经阅读了`typeof`类型守卫并且对JavaScript里的`instanceof`操作符熟悉的话,你可能已经猜到了这节要讲的内容。
241 |
242 | *`instanceof`类型守卫*是通过构造函数来细化类型的一种方式。
243 | 比如,我们借鉴一下之前字符串填充的例子:
244 |
245 | ```ts
246 | interface Padder {
247 | getPaddingString(): string
248 | }
249 |
250 | class SpaceRepeatingPadder implements Padder {
251 | constructor(private numSpaces: number) { }
252 | getPaddingString() {
253 | return Array(this.numSpaces + 1).join(" ");
254 | }
255 | }
256 |
257 | class StringPadder implements Padder {
258 | constructor(private value: string) { }
259 | getPaddingString() {
260 | return this.value;
261 | }
262 | }
263 |
264 | function getRandomPadder() {
265 | return Math.random() < 0.5 ?
266 | new SpaceRepeatingPadder(4) :
267 | new StringPadder(" ");
268 | }
269 |
270 | // 类型为SpaceRepeatingPadder | StringPadder
271 | let padder: Padder = getRandomPadder();
272 |
273 | if (padder instanceof SpaceRepeatingPadder) {
274 | padder; // 类型细化为'SpaceRepeatingPadder'
275 | }
276 | if (padder instanceof StringPadder) {
277 | padder; // 类型细化为'StringPadder'
278 | }
279 | ```
280 |
281 | `instanceof`的右侧要求是一个构造函数,TypeScript将细化为:
282 |
283 | 1. 此构造函数的`prototype`属性的类型,如果它的类型不为`any`的话
284 | 2. 构造签名所返回的类型的联合
285 |
286 | 以此顺序。
287 |
288 | # 可以为`null`的类型
289 |
290 | TypeScript具有两种特殊的类型,`null`和`undefined`,它们分别具有值`null`和`undefined`.
291 | 我们在[基础类型](./Basic%20Types.md)一节里已经做过简要说明。
292 | 默认情况下,类型检查器认为`null`与`undefined`可以赋值给任何类型。
293 | `null`与`undefined`是所有其它类型的一个有效值。
294 | 这也意味着,你阻止不了将它们赋值给其它类型,就算是你想要阻止这种情况也不行。
295 | `null`的发明者,Tony Hoare,称它为[价值亿万美金的错误](https://en.wikipedia.org/wiki/Null_pointer#History)。
296 |
297 | `--strictNullChecks`标记可以解决此错误:当你声明一个变量时,它不会自动地包含`null`或`undefined`。
298 | 你可以使用联合类型明确的包含它们:
299 |
300 | ```ts
301 | let s = "foo";
302 | s = null; // 错误, 'null'不能赋值给'string'
303 | let sn: string | null = "bar";
304 | sn = null; // 可以
305 |
306 | sn = undefined; // error, 'undefined'不能赋值给'string | null'
307 | ```
308 |
309 | 注意,按照JavaScript的语义,TypeScript会把`null`和`undefined`区别对待。
310 | `string | null`,`string | undefined`和`string | undefined | null`是不同的类型。
311 |
312 | ## 可选参数和可选属性
313 |
314 | 使用了`--strictNullChecks`,可选参数会被自动地加上`| undefined`:
315 |
316 | ```ts
317 | function f(x: number, y?: number) {
318 | return x + (y || 0);
319 | }
320 | f(1, 2);
321 | f(1);
322 | f(1, undefined);
323 | f(1, null); // error, 'null' is not assignable to 'number | undefined'
324 | ```
325 |
326 | 可选属性也会有同样的处理:
327 |
328 | ```ts
329 | class C {
330 | a: number;
331 | b?: number;
332 | }
333 | let c = new C();
334 | c.a = 12;
335 | c.a = undefined; // error, 'undefined' is not assignable to 'number'
336 | c.b = 13;
337 | c.b = undefined; // ok
338 | c.b = null; // error, 'null' is not assignable to 'number | undefined'
339 | ```
340 |
341 | ## 类型守卫和类型断言
342 |
343 | 由于可以为`null`的类型是通过联合类型实现,那么你需要使用类型守卫来去除`null`。
344 | 幸运地是这与在JavaScript里写的代码一致:
345 |
346 | ```ts
347 | function f(sn: string | null): string {
348 | if (sn == null) {
349 | return "default";
350 | }
351 | else {
352 | return sn;
353 | }
354 | }
355 | ```
356 |
357 | 这里很明显地去除了`null`,你也可以使用短路运算符:
358 |
359 | ```ts
360 | function f(sn: string | null): string {
361 | return sn || "default";
362 | }
363 | ```
364 |
365 | 如果编译器不能够去除`null`或`undefined`,你可以使用类型断言手动去除。
366 | 语法是添加`!`后缀:`identifier!`从`identifier`的类型里去除了`null`和`undefined`:
367 |
368 | ```ts
369 | function broken(name: string | null): string {
370 | function postfix(epithet: string) {
371 | return name.charAt(0) + '. the ' + epithet; // error, 'name' is possibly null
372 | }
373 | name = name || "Bob";
374 | return postfix("great");
375 | }
376 |
377 | function fixed(name: string | null): string {
378 | function postfix(epithet: string) {
379 | return name!.charAt(0) + '. the ' + epithet; // ok
380 | }
381 | name = name || "Bob";
382 | return postfix("great");
383 | }
384 | ```
385 |
386 | 本例使用了嵌套函数,因为编译器无法去除嵌套函数的`null`(除非是立即调用的函数表达式)。
387 | 因为它无法跟踪所有对嵌套函数的调用,尤其是你将内层函数做为外层函数的返回值。
388 | 如果无法知道函数在哪里被调用,就无法知道调用时`name`的类型。
389 |
390 | # 类型别名
391 |
392 | 类型别名会给一个类型起个新名字。
393 | 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。
394 |
395 | ```ts
396 | type Name = string;
397 | type NameResolver = () => string;
398 | type NameOrResolver = Name | NameResolver;
399 | function getName(n: NameOrResolver): Name {
400 | if (typeof n === 'string') {
401 | return n;
402 | }
403 | else {
404 | return n();
405 | }
406 | }
407 | ```
408 |
409 | 起别名不会新建一个类型 - 它创建了一个新*名字*来引用那个类型。
410 | 给原始类型起别名通常没什么用,尽管可以做为文档的一种形式使用。
411 |
412 | 同接口一样,类型别名也可以是泛型 - 我们可以添加类型参数并且在别名声明的右侧传入:
413 |
414 | ```ts
415 | type Container = { value: T };
416 | ```
417 |
418 | 我们也可以使用类型别名来在属性里引用自己:
419 |
420 | ```ts
421 | type Tree = {
422 | value: T;
423 | left: Tree;
424 | right: Tree;
425 | }
426 | ```
427 |
428 | 与交叉类型一起使用,我们可以创建出一些十分稀奇古怪的类型。
429 |
430 | ```ts
431 | type LinkedList = T & { next: LinkedList };
432 |
433 | interface Person {
434 | name: string;
435 | }
436 |
437 | var people: LinkedList;
438 | var s = people.name;
439 | var s = people.next.name;
440 | var s = people.next.next.name;
441 | var s = people.next.next.next.name;
442 | ```
443 |
444 | 然而,类型别名不能出现在声明右侧的任何地方。
445 |
446 | ```ts
447 | type Yikes = Array; // error
448 | ```
449 |
450 | ## 接口 vs. 类型别名
451 |
452 | 像我们提到的,类型别名可以像接口一样;然而,仍有一些细微差别。
453 |
454 | 其一,接口创建了一个新的名字,可以在其它任何地方使用。
455 | 类型别名并不创建新名字—比如,错误信息就不会使用别名。
456 | 在下面的示例代码里,在编译器中将鼠标悬停在`interfaced`上,显示它返回的是`Interface`,但悬停在`aliased`上时,显示的却是对象字面量类型。
457 |
458 | ```ts
459 | type Alias = { num: number }
460 | interface Interface {
461 | num: number;
462 | }
463 | declare function aliased(arg: Alias): Alias;
464 | declare function interfaced(arg: Interface): Interface;
465 | ```
466 |
467 | 另一个重要区别是类型别名不能被`extends`和`implements`(自己也不能`extends`和`implements`其它类型)。
468 | 因为[软件中的对象应该对于扩展是开放的,但是对于修改是封闭的](https://en.wikipedia.org/wiki/Open/closed_principle),你应该尽量去使用接口代替类型别名。
469 |
470 | 另一方面,如果你无法通过接口来描述一个类型并且需要使用联合类型或元组类型,这时通常会使用类型别名。
471 |
472 | # 字符串字面量类型
473 |
474 | 字符串字面量类型允许你指定字符串必须的固定值。
475 | 在实际应用中,字符串字面量类型可以与联合类型,类型守卫和类型别名很好的配合。
476 | 通过结合使用这些特性,你可以实现类似枚举类型的字符串。
477 |
478 | ```ts
479 | type Easing = "ease-in" | "ease-out" | "ease-in-out";
480 | class UIElement {
481 | animate(dx: number, dy: number, easing: Easing) {
482 | if (easing === "ease-in") {
483 | // ...
484 | }
485 | else if (easing === "ease-out") {
486 | }
487 | else if (easing === "ease-in-out") {
488 | }
489 | else {
490 | // error! should not pass null or undefined.
491 | }
492 | }
493 | }
494 |
495 | let button = new UIElement();
496 | button.animate(0, 0, "ease-in");
497 | button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here
498 | ```
499 |
500 | 你只能从三种允许的字符中选择其一来做为参数传递,传入其它值则会产生错误。
501 |
502 | ```text
503 | Argument of type '"uneasy"' is not assignable to parameter of type '"ease-in" | "ease-out" | "ease-in-out"'
504 | ```
505 |
506 | 字符串字面量类型还可以用于区分函数重载:
507 |
508 | ```ts
509 | function createElement(tagName: "img"): HTMLImageElement;
510 | function createElement(tagName: "input"): HTMLInputElement;
511 | // ... more overloads ...
512 | function createElement(tagName: string): Element {
513 | // ... code goes here ...
514 | }
515 | ```
516 |
517 | # 数字字面量类型
518 |
519 | TypeScript还具有数字字面量类型。
520 |
521 | ```ts
522 | function rollDie(): 1 | 2 | 3 | 4 | 5 | 6 {
523 | // ...
524 | }
525 | ```
526 |
527 | 我们很少直接这样使用,但它们可以用在缩小范围调试bug的时候:
528 |
529 | ```ts
530 | function foo(x: number) {
531 | if (x !== 1 || x !== 2) {
532 | // ~~~~~~~
533 | // Operator '!==' cannot be applied to types '1' and '2'.
534 | }
535 | }
536 | ```
537 |
538 | 换句话说,当`x`与`2`进行比较的时候,它的值必须为`1`,这就意味着上面的比较检查是非法的。
539 |
540 | # 枚举成员类型
541 |
542 | 如我们在[枚举](./Enums.md#union-enums-and-enum-member-types)一节里提到的,当每个枚举成员都是用字面量初始化的时候枚举成员是具有类型的。
543 |
544 | 在我们谈及“单例类型”的时候,多数是指枚举成员类型和数字/字符串字面量类型,尽管大多数用户会互换使用“单例类型”和“字面量类型”。
545 |
546 | # 可辨识联合(Discriminated Unions)
547 |
548 | 你可以合并单例类型,联合类型,类型守卫和类型别名来创建一个叫做*可辨识联合*的高级模式,它也称做*标签联合*或*代数数据类型*。
549 | 可辨识联合在函数式编程里很有用处。
550 | 一些语言会自动地为你辨识联合;而TypeScript则基于已有的JavaScript模式。
551 | 它具有3个要素:
552 |
553 | 1. 具有普通的单例类型属性—*可辨识的特征*。
554 | 2. 一个类型别名包含了那些类型的联合—*联合*。
555 | 3. 此属性上的类型守卫。
556 |
557 | ```ts
558 | interface Square {
559 | kind: "square";
560 | size: number;
561 | }
562 | interface Rectangle {
563 | kind: "rectangle";
564 | width: number;
565 | height: number;
566 | }
567 | interface Circle {
568 | kind: "circle";
569 | radius: number;
570 | }
571 | ```
572 |
573 | 首先我们声明了将要联合的接口。
574 | 每个接口都有`kind`属性但有不同的字符串字面量类型。
575 | `kind`属性称做*可辨识的特征*或*标签*。
576 | 其它的属性则特定于各个接口。
577 | 注意,目前各个接口间是没有联系的。
578 | 下面我们把它们联合到一起:
579 |
580 | ```ts
581 | type Shape = Square | Rectangle | Circle;
582 | ```
583 |
584 | 现在我们使用可辨识联合:
585 |
586 | ```ts
587 | function area(s: Shape) {
588 | switch (s.kind) {
589 | case "square": return s.size * s.size;
590 | case "rectangle": return s.height * s.width;
591 | case "circle": return Math.PI * s.radius ** 2;
592 | }
593 | }
594 | ```
595 |
596 | ## 完整性检查
597 |
598 | 当没有涵盖所有可辨识联合的变化时,我们想让编译器可以通知我们。
599 | 比如,如果我们添加了`Triangle`到`Shape`,我们同时还需要更新`area`:
600 |
601 | ```ts
602 | type Shape = Square | Rectangle | Circle | Triangle;
603 | function area(s: Shape) {
604 | switch (s.kind) {
605 | case "square": return s.size * s.size;
606 | case "rectangle": return s.height * s.width;
607 | case "circle": return Math.PI * s.radius ** 2;
608 | }
609 | // should error here - we didn't handle case "triangle"
610 | }
611 | ```
612 |
613 | 有两种方式可以实现。
614 | 首先是启用`--strictNullChecks`并且指定一个返回值类型:
615 |
616 | ```ts
617 | function area(s: Shape): number { // error: returns number | undefined
618 | switch (s.kind) {
619 | case "square": return s.size * s.size;
620 | case "rectangle": return s.height * s.width;
621 | case "circle": return Math.PI * s.radius ** 2;
622 | }
623 | }
624 | ```
625 |
626 | 因为`switch`没有包含所有情况,所以TypeScript认为这个函数有时候会返回`undefined`。
627 | 如果你明确地指定了返回值类型为`number`,那么你会看到一个错误,因为实际上返回值的类型为`number | undefined`。
628 | 然而,这种方法存在些微妙之处且`--strictNullChecks`对旧代码支持不好。
629 |
630 | 第二种方法使用`never`类型,编译器用它来进行完整性检查:
631 |
632 | ```ts
633 | function assertNever(x: never): never {
634 | throw new Error("Unexpected object: " + x);
635 | }
636 | function area(s: Shape) {
637 | switch (s.kind) {
638 | case "square": return s.size * s.size;
639 | case "rectangle": return s.height * s.width;
640 | case "circle": return Math.PI * s.radius ** 2;
641 | default: return assertNever(s); // error here if there are missing cases
642 | }
643 | }
644 | ```
645 |
646 | 这里,`assertNever`检查`s`是否为`never`类型—即为除去所有可能情况后剩下的类型。
647 | 如果你忘记了某个case,那么`s`将具有一个真实的类型并且你会得到一个错误。
648 | 这种方式需要你定义一个额外的函数,但是在你忘记某个case的时候也更加明显。
649 |
650 | # 多态的`this`类型
651 |
652 | 多态的`this`类型表示的是某个包含类或接口的*子类型*。
653 | 这被称做*F*-bounded多态性。
654 | 它能很容易的表现连贯接口间的继承,比如。
655 | 在计算器的例子里,在每个操作之后都返回`this`类型:
656 |
657 | ```ts
658 | class BasicCalculator {
659 | public constructor(protected value: number = 0) { }
660 | public currentValue(): number {
661 | return this.value;
662 | }
663 | public add(operand: number): this {
664 | this.value += operand;
665 | return this;
666 | }
667 | public multiply(operand: number): this {
668 | this.value *= operand;
669 | return this;
670 | }
671 | // ... other operations go here ...
672 | }
673 |
674 | let v = new BasicCalculator(2)
675 | .multiply(5)
676 | .add(1)
677 | .currentValue();
678 | ```
679 |
680 | 由于这个类使用了`this`类型,你可以继承它,新的类可以直接使用之前的方法,不需要做任何的改变。
681 |
682 | ```ts
683 | class ScientificCalculator extends BasicCalculator {
684 | public constructor(value = 0) {
685 | super(value);
686 | }
687 | public sin() {
688 | this.value = Math.sin(this.value);
689 | return this;
690 | }
691 | // ... other operations go here ...
692 | }
693 |
694 | let v = new ScientificCalculator(2)
695 | .multiply(5)
696 | .sin()
697 | .add(1)
698 | .currentValue();
699 | ```
700 |
701 | 如果没有`this`类型,`ScientificCalculator`就不能够在继承`BasicCalculator`的同时还保持接口的连贯性。
702 | `multiply`将会返回`BasicCalculator`,它并没有`sin`方法。
703 | 然而,使用`this`类型,`multiply`会返回`this`,在这里就是`ScientificCalculator`。
704 |
705 | # 索引类型(Index types)
706 |
707 | 使用索引类型,编译器就能够检查使用了动态属性名的代码。
708 | 例如,一个常见的JavaScript模式是从对象中选取属性的子集。
709 |
710 | ```js
711 | function pluck(o, names) {
712 | return names.map(n => o[n]);
713 | }
714 | ```
715 |
716 | 下面是如何在TypeScript里使用此函数,通过**索引类型查询**和**索引访问**操作符:
717 |
718 | ```ts
719 | function pluck(o: T, names: K[]): T[K][] {
720 | return names.map(n => o[n]);
721 | }
722 |
723 | interface Person {
724 | name: string;
725 | age: number;
726 | }
727 | let person: Person = {
728 | name: 'Jarid',
729 | age: 35
730 | };
731 | let strings: string[] = pluck(person, ['name']); // ok, string[]
732 | ```
733 |
734 | 编译器会检查`name`是否真的是`Person`的一个属性。
735 | 本例还引入了几个新的类型操作符。
736 | 首先是`keyof T`,**索引类型查询操作符**。
737 | 对于任何类型`T`,`keyof T`的结果为`T`上已知的公共属性名的联合。
738 | 例如:
739 |
740 | ```ts
741 | let personProps: keyof Person; // 'name' | 'age'
742 | ```
743 |
744 | `keyof Person`是完全可以与`'name' | 'age'`互相替换的。
745 | 不同的是如果你添加了其它的属性到`Person`,例如`address: string`,那么`keyof Person`会自动变为`'name' | 'age' | 'address'`。
746 | 你可以在像`pluck`函数这类上下文里使用`keyof`,因为在使用之前你并不清楚可能出现的属性名。
747 | 但编译器会检查你是否传入了正确的属性名给`pluck`:
748 |
749 | ```ts
750 | pluck(person, ['age', 'unknown']); // error, 'unknown' is not in 'name' | 'age'
751 | ```
752 |
753 | 第二个操作符是`T[K]`,**索引访问操作符**。
754 | 在这里,类型语法反映了表达式语法。
755 | 这意味着`person['name']`具有类型`Person['name']` — 在我们的例子里则为`string`类型。
756 | 然而,就像索引类型查询一样,你可以在普通的上下文里使用`T[K]`,这正是它的强大所在。
757 | 你只要确保类型变量`K extends keyof T`就可以了。
758 | 例如下面`getProperty`函数的例子:
759 |
760 | ```ts
761 | function getProperty(o: T, name: K): T[K] {
762 | return o[name]; // o[name] is of type T[K]
763 | }
764 | ```
765 |
766 | `getProperty`里的`o: T`和`name: K`,意味着`o[name]: T[K]`。
767 | 当你返回`T[K]`的结果,编译器会实例化键的真实类型,因此`getProperty`的返回值类型会随着你需要的属性改变。
768 |
769 | ```ts
770 | let name: string = getProperty(person, 'name');
771 | let age: number = getProperty(person, 'age');
772 | let unknown = getProperty(person, 'unknown'); // error, 'unknown' is not in 'name' | 'age'
773 | ```
774 |
775 | ## 索引类型和字符串索引签名
776 |
777 | `keyof`和`T[K]`与字符串索引签名进行交互。
778 | 如果你有一个带有字符串索引签名的类型,那么`keyof T`会是`string`。
779 | 并且`T[string]`为索引签名的类型:
780 |
781 | ```ts
782 | interface Map {
783 | [key: string]: T;
784 | }
785 | let keys: keyof Map; // string
786 | let value: Map['foo']; // number
787 | ```
788 |
789 | # 映射类型
790 |
791 | 一个常见的任务是将一个已知的类型每个属性都变为可选的:
792 |
793 | ```ts
794 | interface PersonPartial {
795 | name?: string;
796 | age?: number;
797 | }
798 | ```
799 |
800 | 或者我们想要一个只读版本:
801 |
802 | ```ts
803 | interface PersonReadonly {
804 | readonly name: string;
805 | readonly age: number;
806 | }
807 | ```
808 |
809 | 这在JavaScript里经常出现,TypeScript提供了从旧类型中创建新类型的一种方式 — **映射类型**。
810 | 在映射类型里,新类型以相同的形式去转换旧类型里每个属性。
811 | 例如,你可以令每个属性成为`readonly`类型或可选的。
812 | 下面是一些例子:
813 |
814 | ```ts
815 | type Readonly = {
816 | readonly [P in keyof T]: T[P];
817 | }
818 | type Partial = {
819 | [P in keyof T]?: T[P];
820 | }
821 | ```
822 |
823 | 像下面这样使用:
824 |
825 | ```ts
826 | type PersonPartial = Partial;
827 | type ReadonlyPerson = Readonly;
828 | ```
829 |
830 | 下面来看看最简单的映射类型和它的组成部分:
831 |
832 | ```ts
833 | type Keys = 'option1' | 'option2';
834 | type Flags = { [K in Keys]: boolean };
835 | ```
836 |
837 | 它的语法与索引签名的语法类型,内部使用了`for .. in`。
838 | 具有三个部分:
839 |
840 | 1. 类型变量`K`,它会依次绑定到每个属性。
841 | 2. 字符串字面量联合的`Keys`,它包含了要迭代的属性名的集合。
842 | 3. 属性的结果类型。
843 |
844 | 在个简单的例子里,`Keys`是硬编码的属性名列表并且属性类型永远是`boolean`,因此这个映射类型等同于:
845 |
846 | ```ts
847 | type Flags = {
848 | option1: boolean;
849 | option2: boolean;
850 | }
851 | ```
852 |
853 | 在真正的应用里,可能不同于上面的`Readonly`或`Partial`。
854 | 它们会基于一些已存在的类型,且按照一定的方式转换字段。
855 | 这就是`keyof`和索引访问类型要做的事情:
856 |
857 | ```ts
858 | type NullablePerson = { [P in keyof Person]: Person[P] | null }
859 | type PartialPerson = { [P in keyof Person]?: Person[P] }
860 | ```
861 |
862 | 但它更有用的地方是可以有一些通用版本。
863 |
864 | ```ts
865 | type Nullable = { [P in keyof T]: T[P] | null }
866 | type Partial = { [P in keyof T]?: T[P] }
867 | ```
868 |
869 | 在这些例子里,属性列表是`keyof T`且结果类型是`T[P]`的变体。
870 | 这是使用通用映射类型的一个好模版。
871 | 因为这类转换是[同态](https://en.wikipedia.org/wiki/Homomorphism)的,映射只作用于`T`的属性而没有其它的。
872 | 编译器知道在添加任何新属性之前可以拷贝所有存在的属性修饰符。
873 | 例如,假设`Person.name`是只读的,那么`Partial.name`也将是只读的且为可选的。
874 |
875 | 下面是另一个例子,`T[P]`被包装在`Proxy`类里:
876 |
877 | ```ts
878 | type Proxy = {
879 | get(): T;
880 | set(value: T): void;
881 | }
882 | type Proxify = {
883 | [P in keyof T]: Proxy;
884 | }
885 | function proxify(o: T): Proxify {
886 | // ... wrap proxies ...
887 | }
888 | let proxyProps = proxify(props);
889 | ```
890 |
891 | 注意`Readonly`和`Partial`用处不小,因此它们与`Pick`和`Record`一同被包含进了TypeScript的标准库里:
892 |
893 | ```ts
894 | type Pick = {
895 | [P in K]: T[P];
896 | }
897 | type Record = {
898 | [P in K]: T;
899 | }
900 | ```
901 |
902 | `Readonly`,`Partial`和`Pick`是同态的,但`Record`不是。
903 | 因为`Record`并不需要输入类型来拷贝属性,所以它不属于同态:
904 |
905 | ```ts
906 | type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
907 | ```
908 |
909 | 非同态类型本质上会创建新的属性,因此它们不会从它处拷贝属性修饰符。
910 |
911 | ## 由映射类型进行推断
912 |
913 | 现在你了解了如何包装一个类型的属性,那么接下来就是如何拆包。
914 | 其实这也非常容易:
915 |
916 | ```ts
917 | function unproxify(t: Proxify): T {
918 | let result = {} as T;
919 | for (const k in t) {
920 | result[k] = t[k].get();
921 | }
922 | return result;
923 | }
924 |
925 | let originalProps = unproxify(proxyProps);
926 | ```
927 |
928 | 注意这个拆包推断只适用于同态的映射类型。
929 | 如果映射类型不是同态的,那么需要给拆包函数一个明确的类型参数。
930 |
931 | ## 有条件类型
932 |
933 | TypeScript 2.8引入了*有条件类型*,它能够表示非统一的类型。
934 | 有条件的类型会以一个条件表达式进行类型关系检测,从而在两种类型中选择其一:
935 |
936 | ```ts
937 | T extends U ? X : Y
938 | ```
939 |
940 | 上面的类型意思是,若`T`能够赋值给`U`,那么类型是`X`,否则为`Y`。
941 |
942 | 有条件的类型`T extends U ? X : Y`或者*解析*为`X`,或者*解析*为`Y`,再或者*延迟*解析,因为它可能依赖一个或多个类型变量。
943 | 若`T`或`U`包含类型参数,那么是否解析为`X`或`Y`或推迟,取决于类型系统是否有足够的信息来确定`T`总是可以赋值给`U`。
944 |
945 | 下面是一些类型可以被立即解析的例子:
946 |
947 | ```ts
948 | declare function f(x: T): T extends true ? string : number;
949 |
950 | // Type is 'string | number
951 | let x = f(Math.random() < 0.5)
952 |
953 | ```
954 |
955 | 另外一个例子涉及`TypeName`类型别名,它使用了嵌套了有条件类型:
956 |
957 | ```ts
958 | type TypeName =
959 | T extends string ? "string" :
960 | T extends number ? "number" :
961 | T extends boolean ? "boolean" :
962 | T extends undefined ? "undefined" :
963 | T extends Function ? "function" :
964 | "object";
965 |
966 | type T0 = TypeName; // "string"
967 | type T1 = TypeName<"a">; // "string"
968 | type T2 = TypeName; // "boolean"
969 | type T3 = TypeName<() => void>; // "function"
970 | type T4 = TypeName; // "object"
971 | ```
972 |
973 | 下面是一个有条件类型被推迟解析的例子:
974 |
975 | ```ts
976 | interface Foo {
977 | propA: boolean;
978 | propB: boolean;
979 | }
980 |
981 | declare function f(x: T): T extends Foo ? string : number;
982 |
983 | function foo(x: U) {
984 | // Has type 'U extends Foo ? string : number'
985 | let a = f(x);
986 |
987 | // This assignment is allowed though!
988 | let b: string | number = a;
989 | }
990 | ```
991 |
992 | 这里,`a`变量含有未确定的有条件类型。
993 | 当有另一段代码调用`foo`,它会用其它类型替换`U`,TypeScript将重新计算有条件类型,决定它是否可以选择一个分支。
994 |
995 | 与此同时,我们可以将有条件类型赋值给其它类型,只要有条件类型的每个分支都可以赋值给目标类型。
996 | 因此在我们的例子里,我们可以将`U extends Foo ? string : number`赋值给`string | number`,因为不管这个有条件类型最终结果是什么,它只能是`string`或`number`。
997 |
998 | ### 分布式有条件类型
999 |
1000 | 如果有条件类型里待检查的类型是`naked type parameter`,那么它也被称为“分布式有条件类型”。
1001 | 分布式有条件类型在实例化时会自动分发成联合类型。
1002 | 例如,实例化`T extends U ? X : Y`,`T`的类型为`A | B | C`,会被解析为`(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)`。
1003 |
1004 | #### 例子
1005 |
1006 | ```ts
1007 | type T10 = TypeName void)>; // "string" | "function"
1008 | type T12 = TypeName; // "string" | "object" | "undefined"
1009 | type T11 = TypeName; // "object"
1010 | ```
1011 |
1012 | 在`T extends U ? X : Y`的实例化里,对`T`的引用被解析为联合类型的一部分(比如,`T`指向某一单个部分,在有条件类型分布到联合类型之后)。
1013 | 此外,在`X`内对`T`的引用有一个附加的类型参数约束`U`(例如,`T`被当成在`X`内可赋值给`U`)。
1014 |
1015 | #### 例子
1016 |
1017 | ```ts
1018 | type BoxedValue = { value: T };
1019 | type BoxedArray = { array: T[] };
1020 | type Boxed = T extends any[] ? BoxedArray : BoxedValue;
1021 |
1022 | type T20 = Boxed; // BoxedValue;
1023 | type T21 = Boxed; // BoxedArray;
1024 | type T22 = Boxed; // BoxedValue | BoxedArray;
1025 | ```
1026 |
1027 | 注意在`Boxed`的`true`分支里,`T`有个额外的约束`any[]`,因此它适用于`T[number]`数组元素类型。同时也注意一下有条件类型是如何分布成联合类型的。
1028 |
1029 | 有条件类型的分布式的属性可以方便地用来*过滤*联合类型:
1030 |
1031 | ```ts
1032 | type Diff = T extends U ? never : T; // Remove types from T that are assignable to U
1033 | type Filter = T extends U ? T : never; // Remove types from T that are not assignable to U
1034 |
1035 | type T30 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
1036 | type T31 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
1037 | type T32 = Diff void), Function>; // string | number
1038 | type T33 = Filter void), Function>; // () => void
1039 |
1040 | type NonNullable = Diff; // Remove null and undefined from T
1041 |
1042 | type T34 = NonNullable; // string | number
1043 | type T35 = NonNullable; // string | string[]
1044 |
1045 | function f1(x: T, y: NonNullable) {
1046 | x = y; // Ok
1047 | y = x; // Error
1048 | }
1049 |
1050 | function f2(x: T, y: NonNullable) {
1051 | x = y; // Ok
1052 | y = x; // Error
1053 | let s1: string = x; // Error
1054 | let s2: string = y; // Ok
1055 | }
1056 | ```
1057 |
1058 | 有条件类型与映射类型结合时特别有用:
1059 |
1060 | ```ts
1061 | type FunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
1062 | type FunctionProperties = Pick>;
1063 |
1064 | type NonFunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
1065 | type NonFunctionProperties = Pick>;
1066 |
1067 | interface Part {
1068 | id: number;
1069 | name: string;
1070 | subparts: Part[];
1071 | updatePart(newName: string): void;
1072 | }
1073 |
1074 | type T40 = FunctionPropertyNames; // "updatePart"
1075 | type T41 = NonFunctionPropertyNames; // "id" | "name" | "subparts"
1076 | type T42 = FunctionProperties; // { updatePart(newName: string): void }
1077 | type T43 = NonFunctionProperties; // { id: number, name: string, subparts: Part[] }
1078 | ```
1079 |
1080 | 与联合类型和交叉类型相似,有条件类型不允许递归地引用自己。比如下面的错误。
1081 |
1082 | #### 例子
1083 |
1084 | ```ts
1085 | type ElementType = T extends any[] ? ElementType : T; // Error
1086 | ```
1087 |
1088 | ### 有条件类型中的类型推断
1089 |
1090 | 现在在有条件类型的`extends`子语句中,允许出现`infer`声明,它会引入一个待推断的类型变量。
1091 | 这个推断的类型变量可以在有条件类型的true分支中被引用。
1092 | 允许出现多个同类型变量的`infer`。
1093 |
1094 | 例如,下面代码会提取函数类型的返回值类型:
1095 |
1096 | ```ts
1097 | type ReturnType = T extends (...args: any[]) => infer R ? R : any;
1098 | ```
1099 |
1100 | 有条件类型可以嵌套来构成一系列的匹配模式,按顺序进行求值:
1101 |
1102 | ```ts
1103 | type Unpacked =
1104 | T extends (infer U)[] ? U :
1105 | T extends (...args: any[]) => infer U ? U :
1106 | T extends Promise ? U :
1107 | T;
1108 |
1109 | type T0 = Unpacked; // string
1110 | type T1 = Unpacked; // string
1111 | type T2 = Unpacked<() => string>; // string
1112 | type T3 = Unpacked>; // string
1113 | type T4 = Unpacked[]>; // Promise
1114 | type T5 = Unpacked[]>>; // string
1115 | ```
1116 |
1117 | 下面的例子解释了在协变位置上,同一个类型变量的多个候选类型会被推断为联合类型:
1118 |
1119 | ```ts
1120 | type Foo = T extends { a: infer U, b: infer U } ? U : never;
1121 | type T10 = Foo<{ a: string, b: string }>; // string
1122 | type T11 = Foo<{ a: string, b: number }>; // string | number
1123 | ```
1124 |
1125 | 相似地,在抗变位置上,同一个类型变量的多个候选类型会被推断为交叉类型:
1126 |
1127 | ```ts
1128 | type Bar = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never;
1129 | type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>; // string
1130 | type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>; // string & number
1131 | ```
1132 |
1133 | 当推断具有多个调用签名(例如函数重载类型)的类型时,用*最后*的签名(大概是最自由的包含所有情况的签名)进行推断。
1134 | 无法根据参数类型列表来解析重载。
1135 |
1136 | ```ts
1137 | declare function foo(x: string): number;
1138 | declare function foo(x: number): string;
1139 | declare function foo(x: string | number): string | number;
1140 | type T30 = ReturnType; // string | number
1141 | ```
1142 |
1143 | 无法在正常类型参数的约束子语句中使用`infer`声明:
1144 |
1145 | ```ts
1146 | type ReturnType infer R> = R; // 错误,不支持
1147 | ```
1148 |
1149 | 但是,可以这样达到同样的效果,在约束里删掉类型变量,用有条件类型替换:
1150 |
1151 | ```ts
1152 | type AnyFunction = (...args: any[]) => any;
1153 | type ReturnType = T extends (...args: any[]) => infer R ? R : any;
1154 | ```
1155 |
1156 | ### 预定义的有条件类型
1157 |
1158 | TypeScript 2.8在`lib.d.ts`里增加了一些预定义的有条件类型:
1159 |
1160 | * `Exclude` -- 从`T`中剔除可以赋值给`U`的类型。
1161 | * `Extract` -- 提取`T`中可以赋值给`U`的类型。
1162 | * `NonNullable` -- 从`T`中剔除`null`和`undefined`。
1163 | * `ReturnType` -- 获取函数返回值类型。
1164 | * `InstanceType` -- 获取构造函数类型的实例类型。
1165 |
1166 | #### Example
1167 |
1168 | ```ts
1169 | type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
1170 | type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
1171 |
1172 | type T02 = Exclude void), Function>; // string | number
1173 | type T03 = Extract void), Function>; // () => void
1174 |
1175 | type T04 = NonNullable; // string | number
1176 | type T05 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[]
1177 |
1178 | function f1(s: string) {
1179 | return { a: 1, b: s };
1180 | }
1181 |
1182 | class C {
1183 | x = 0;
1184 | y = 0;
1185 | }
1186 |
1187 | type T10 = ReturnType<() => string>; // string
1188 | type T11 = ReturnType<(s: string) => void>; // void
1189 | type T12 = ReturnType<(() => T)>; // {}
1190 | type T13 = ReturnType<(() => T)>; // number[]
1191 | type T14 = ReturnType; // { a: number, b: string }
1192 | type T15 = ReturnType; // any
1193 | type T16 = ReturnType; // any
1194 | type T17 = ReturnType; // Error
1195 | type T18 = ReturnType; // Error
1196 |
1197 | type T20 = InstanceType; // C
1198 | type T21 = InstanceType; // any
1199 | type T22 = InstanceType; // any
1200 | type T23 = InstanceType; // Error
1201 | type T24 = InstanceType; // Error
1202 | ```
1203 |
1204 | > 注意:`Exclude`类型是[建议的](https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-307871458)`Diff`类型的一种实现。我们使用`Exclude`这个名字是为了避免破坏已经定义了`Diff`的代码,并且我们感觉这个名字能更好地表达类型的语义。我们没有增加`Omit`类型,因为它可以很容易的用`Pick>`来表示。
1205 |
--------------------------------------------------------------------------------
/9.TypeScript装饰器/README.md:
--------------------------------------------------------------------------------
1 | # TypeScript装饰器
2 |
3 | 随着TypeScript和ES6里引入了类,在一些场景下我们需要额外的特性来支持标注或修改类及其成员。 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。 Javascript里的装饰器目前处在 建议征集的第二阶段,但在TypeScript里已做为一项实验性特性予以支持。
4 |
5 | 若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选项:
6 |
7 | * 命令行:
8 |
9 | ```ts
10 | tsc --target ES5 --experimentalDecorators
11 | ```
12 |
13 | * tsconfig.json:
14 |
15 | ```ts
16 | {
17 | "compilerOptions": {
18 | "target": "ES5",
19 | "experimentalDecorators": true
20 | }
21 | }
22 | ```
23 |
24 | ## 装饰器
25 |
26 | 装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。
27 |
28 | 语法:
29 |
30 | * 首先定义装饰器函数:
31 |
32 | 定义装饰器函数,里面写一些扩展功能,这个函数会在该装饰器被使用的时候调用。
33 |
34 | * 使用装饰器:
35 |
36 | 在需要被装饰的类或方法前通过`@expression`这种形式,也就是`@装饰器函数`来调用装饰器。
37 |
38 | 下面是使用类装饰器(@Age)的例子,应用在Cat和Dog类上:
39 |
40 | 定义@Age装饰器函数:
41 |
42 | ```ts
43 | function Age(v: number) {
44 | // 这个返回的函数才是真正的装饰器要执行的函数,作用是传参
45 | return function(constructor: T): T {
46 | class PersonAge extends constructor {
47 | age: number = v;
48 | }
49 | return PersonAge;
50 | }
51 | }
52 | ```
53 | 在Cat和Dog类上都可以使用装饰器,本来Cat和Dog都没有age属性的,加上装饰器传入参数调用后,就有了age了。
54 |
55 | ```ts
56 | @Age(1)
57 | class Cat {
58 | name = '小猫咪'
59 | }
60 | @Age(2)
61 | class Dog {
62 | name = '小奶狗'
63 | }
64 |
65 | let c1 = new Cat();
66 | console.log(c1); // Animal {name: "小猫咪", age: 1}
67 |
68 | let d1 = new Dog();
69 | console.log(d1); // Animal {name: "小奶狗", age: 2}
70 | ```
71 |
--------------------------------------------------------------------------------
/9.TypeScript装饰器/index.ts:
--------------------------------------------------------------------------------
1 | function Age(v: number) {
2 | // 这个返回的函数才是真正的装饰器要执行的函数,作用是传参
3 | return function(constructor: T): T {
4 | class Animal extends constructor {
5 | age: number = v;
6 | }
7 | return Animal;
8 | }
9 | }
10 |
11 | @Age(1)
12 | class Cat {
13 | name = '小猫咪'
14 | }
15 | @Age(2)
16 | class Dog {
17 | name = '小奶狗'
18 | }
19 |
20 | let c1 = new Cat();
21 | console.log(c1); // Animal {name: "小猫咪", age: 1}
22 |
23 | let d1 = new Dog();
24 | console.log(d1); // Animal {name: "小奶狗", age: 2}
--------------------------------------------------------------------------------
/9.TypeScript装饰器/js/index.js:
--------------------------------------------------------------------------------
1 | var __extends = (this && this.__extends) || (function () {
2 | var extendStatics = function (d, b) {
3 | extendStatics = Object.setPrototypeOf ||
4 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
6 | return extendStatics(d, b);
7 | };
8 | return function (d, b) {
9 | extendStatics(d, b);
10 | function __() { this.constructor = d; }
11 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
12 | };
13 | })();
14 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
15 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18 | return c > 3 && r && Object.defineProperty(target, key, r), r;
19 | };
20 | function Age(v) {
21 | // 这个返回的函数才是真正的装饰器要执行的函数,作用是传参
22 | return function (constructor) {
23 | var Animal = /** @class */ (function (_super) {
24 | __extends(Animal, _super);
25 | function Animal() {
26 | var _this = _super !== null && _super.apply(this, arguments) || this;
27 | _this.age = v;
28 | return _this;
29 | }
30 | return Animal;
31 | }(constructor));
32 | return Animal;
33 | };
34 | }
35 | var Cat = /** @class */ (function () {
36 | function Cat() {
37 | this.name = '小猫咪';
38 | }
39 | Cat = __decorate([
40 | Age(1)
41 | ], Cat);
42 | return Cat;
43 | }());
44 | var Dog = /** @class */ (function () {
45 | function Dog() {
46 | this.name = '小奶狗';
47 | }
48 | Dog = __decorate([
49 | Age(2)
50 | ], Dog);
51 | return Dog;
52 | }());
53 | var c1 = new Cat();
54 | console.log(c1); // Animal {name: "小猫咪", age: 1}
55 | var d1 = new Dog();
56 | console.log(d1); // Animal {name: "小奶狗", age: 2}
57 |
--------------------------------------------------------------------------------
/9.TypeScript装饰器/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "experimentalDecorators": true,
5 | "outDir": "./js"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/9.TypeScript装饰器/typescript装饰器.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 | 内容概述:装饰器
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 dengzhao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # start-with-typescript
2 |
3 | **为什么要学习和使用TypeScript?**
4 |
5 | 当我使用一些常见的前端框架和库,发现现在很多框架和库都已经使用了TypeScript的时候;当我在项目中使用某些库产生了bug,因为时间问题我急着解决问题,自己去找库的bug,然而我发现这些ts编写的文件让我无处下手的时候;当我想看用TypeScript编写的antd design之类的框架源码的时候;当我看见很多react、vue和angular项目用了TypeScript的时候,我发现是时候学习TypeScript了。
6 |
7 | TypeScript是一种纯面向对象的语言。相比JavaScript,它更能提高我们的开发效率。而且,由于TypeScript是JavaScript的超集,TypeScript能够和JavaScript混合使用。因为TypeScript的这个强大特性,越来越多的优秀框架开始尝试使用TypeScript。
8 |
9 | 在编写代码的阶段,TypeScript就能够找到大部分的错误,而JavaScript在这方面就没那么友好了。要知道,运行时错误越少,你的程序的bug就越少。除此之外,相比JavaScript,TypeScript的重构也更容易。
10 |
11 | 供参考文档:
12 |
13 | * [官网](https://www.tslang.cn/docs/home.html)
14 |
15 | * [Github-TypeScript使用手册](https://github.com/zhongsp/TypeScript)
16 |
17 | ## TypeScript基础
18 |
19 | * 1: [TypeScript安装编译](https://github.com/dzfrontend/start-with-typescript/tree/master/1.TypeScript安装编译/)
20 |
21 | * 2: [TypeScript数据类型](https://github.com/dzfrontend/start-with-typescript/tree/master/2.TypeScript数据类型/)
22 |
23 | * 3: [TypeScript函数](https://github.com/dzfrontend/start-with-typescript/tree/master/3.TypeScript函数/)
24 |
25 | * 4: [TypeScript类](https://github.com/dzfrontend/start-with-typescript/tree/master/4.TypeScript类/)
26 |
27 | * 5: [TypeScript接口](https://github.com/dzfrontend/start-with-typescript/tree/master/5.TypeScript接口/)
28 |
29 | * 6: [TypeScript泛型](https://github.com/dzfrontend/start-with-typescript/tree/master/6.TypeScript泛型/)
30 |
31 | * 7: [TypeScript类型推论](https://github.com/dzfrontend/start-with-typescript/tree/master/7.TypeScript类型推论/)
32 |
33 | * 8: [TypeScript高级类型](https://github.com/dzfrontend/start-with-typescript/tree/master/8.TypeScript高级类型/)
34 |
35 | * 9: [TypeScript装饰器](https://github.com/dzfrontend/start-with-typescript/tree/master/9.TypeScript装饰器/)
36 |
37 | ## TypeScript使用
38 |
39 | * 1: [webpack如何编译typescript](https://github.com/dzfrontend/webpack/tree/webpack3/webpack3/3.webpack%E7%BC%96%E8%AF%91typescript)
--------------------------------------------------------------------------------
/images/tsc01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/staticdeng/start-with-typescript/10d280be1350f29c40c403cc81ed2ac551405796/images/tsc01.png
--------------------------------------------------------------------------------
/images/tsc02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/staticdeng/start-with-typescript/10d280be1350f29c40c403cc81ed2ac551405796/images/tsc02.png
--------------------------------------------------------------------------------