const a: number = 1;
const b: string = '1';
const c: number[] = [1, 2, 3]; const d: Array<number> = [1, 2, 3]; const e: any[] = [1, '2', true];
const f: boolean = true;
const g: object = {};
经常使用于组合类型java
const h: number | undefined;
const i: null;
可为数组中的每一个参数定义相对应的类型node
const j: [number, string] = [1, '2'];
enum err { first = 3, 'second', } const k: e = err.first; console.log(g); // 4
tipswebpack
- 若是未赋值的上一个值是数字,那么这个未赋值的值就是上一个值 +1
- 若是未赋值的上一个值未赋值,那么输出的就是它的下标
- 若是未赋值的上一个值是非数字,那么必须赋值
指定方法类型,表示没有返回值,方法体中不能有return
web
function add(): void { console.log('add'); } // 若是方法体有返回值,能够加上返回值的类型 function delete: string { return 'delete'; }
其余类型(包括undefind和null)的子类型,表明从不会出现的值编程
let o: never; o = (() => { throw new Error('error msg'); })();
让参数能够是任何一种类型数组
let p: any = 1; p = '2'; p = true;
function add(): vide {}
function getUserInfo(name: string, age?: number, school: string = '哈佛大学'): string { return `name: ${name}, age: ${age || '年龄不详'}, school: ${string}`; }
tips
?
表明这个参数可不传,不传即为undefined
,也可定义默认值
function sum(a: number, b: number, ...arr: number[]): number { let sum: number = a + b; arr.forEach(i => { sum += i; }); console.log(arr); // [3, 4, 5] return sum; } console.log(sum(1, 2, 3, 4, 5)); // 15
function reload(name: string): void {} function reload(age: number): void {} function reload(info: any): any { if (typeof(info) === 'string') { console.log(`个人名字是: ${info}`); } else if (typeof(info) === 'number') { console.log(`个人年龄是: ${info}`); } } reload('Clearlove'); // 个人名字是Clearlove reload(18); // 个人年龄是18
function reload(name: string): void function reload(name: string, age?: number): void function reload(name: any, age? number): any { if (age) { console.log(`个人名字是: ${name}, 今年${age}岁!!`); } else { console.log(`你们好,个人名字是: ${name}`); } } reload('Clearlove'); // 你们好,个人名字是Clearlove reload('Clearlove', 18); // 个人名字是Clearlove, 今年18岁!!
tips浏览器
- 被重载的函数,是没有函数体的,能够根据参数的类型走其中一个方法并判断参数
- 函数的重载与返回值类型无关
- 函数重载的做用:是一种参数校验功能,在进行函数调用时,会对参数进行检查,只有传人的参数类型、顺序、个数和重载的函数的参数相同时,才能调用成功,不然报错
class Person { // 私有变量 private name: string; // 构造函数 constructor(name: string) { this.name = name; } getName(): string { return this.name; } setName(name: sring): void { this.name = name; } } const myBoy = new Person('Clearlove'); console.log(myBoy.getName()); // Clearlove myBoy.setName('test');
class Son extends Person { // 静态属性 public static age: number = 18; // 学校 public school: string; constructor(name: string, school: string) { // 访问派生类的构造函数前,必须调用“super”,初始化父类构造函数,并把参数传给父类 super(name); this.school = school; } // 静态方法 static run(name: string): string { return `${name}在跑步,他是年龄是${this.age}`; } } const son = new Son('Clearlove', '清华大学'); son.setName('Test'); console.log(son); console.log(Son.run('Clearlove')); // Clearlove在跑步,他的年龄是18 console.log(Son.age); // 18
tipsapp
- public 在当前类、子类和类之外均可以访问
- protected 在当前类、子类内部均可以访问,类外部没法访问
- private 在当前类内部能够访问,子类和类外部没法访问
- 属性不加修饰符,默认都是public
经过抽象方法/方法重载,实现多态。多态的做用是用来定义标准
// 抽象父类ide
abstract class Animal { // 私有属性 private name: string; constructor(name: string) { this.name = name; } // 抽象成员: 方法 abstract eat(): any; // 抽象成员: 属性 protected abstract ages: number; sleep(): void { console.log(`${this.name}在睡觉`); } } class Cat extends Animal { ages: number = 2; constructor(name: string) { super(name); } // 非抽象类: Cat 不会自动实现继承自: Animal类的抽象方法: eat, 必须手动定义父类中的抽象方法,着就是多态 eat(): string { return '猫吃鱼'; } sleep(): string { return '猫在睡觉'; } } const cat = new Cat('Tom'); cat.sleep();
tips函数
- 抽象类没法 实例化
- 非抽象类继承父类时,不会自动实现 来自父类的抽象成员,必须手动定义 父类中的成员,不然会报错
- 抽象成员包含 属性 和 方法
在面向对象的编程中,接口是一种规范的定义,它定义了行为和动做的规范。
在程序设计里面,接口起到了一种限制和规范的做用。
接口定义了某一批类所需遵照的规范,接口没必要关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类必须提供某些方法,提供这些方法的类就能够知足实际须要。ts中的接口相似java,同时还增长了更灵活的接口类型,包括属性、函数、可索引和类等。
interface InterfaceName { first: string; second?: string; } function logParam(name: InterfaceName): viod { console.log(name.first, name.second. 'test'); } const obj = { first: '1', second: '2'. three: '3' }; logParam({ first: '1', second: '2'. three: '3' }); // 报错,只能传接口定义的值 logParam(obj);
tips
用变量存储数据,这样能够传入定义的接口外的值,不然若是直接传入对象中无接口定义的值会报错
#### 函数类型接口
对函数传入的参数类型,以及返回值类型进行约束,可批量进行约束
interface keyMap { (key: string, value: string): string; } let logKeyMap: keyMap = fucntion (key: string, value: string): string { return key + value; } console.log(logKeyMao('key', 'value'));
tips
接口只对传入的参数的类型和参数的个数进行约束,不对参数名称进行约束
interface Arr { [index: number]: string; } let test: Arr = ['123'];
interface Obj { [index: string]: string; } let test: Obj = { name: 'Clearlove' };
tips
- 对 数组 进行约束,index必须是 number类型
- 对 对象 进行约束,index必须是 string类型
- 索引签名参数类型必须为string或者number
interface Ainmal { name: string; eat(): void; } calss Dogs implements Animal { name: string; constructor(name: string) { this.name = name; } eat() {} }
interface Dog { ear(): void; } interface Persons entexds Dog { work(): void; } class Cat { code() { console.log('猫在敲代码'); } } class SuperMan extends Cat implements Persons { eat(): void { console.log('eat'); } work(): void { console.log('work'); } } const man = new SuperMan(); man.code();
tips
类接口会对类的 属性 和 方法进行约束,相似非抽象类继承类时必须实现某些方法和属性,但对于属性和方法的类型约束更加严格。除了方法 void类型 可被 从新定义外,其余属性或方法的类型定义须要和接口保持一致。
软件工程中,咱们不只要建立一致的、定义良好的API, 同时也要考虑重用性。
组件不只可以支持当前的数据类型,同时也能支持将来的数据类型,在建立大型系统时为你提供了十分灵活的功能。
泛型就是解决 类、接口、方法 的 复用性,以及对不特定数据类型的支持。
要求:传入的参数和返回的参数一致
function getDate<T>(value: T): T { return value; } const val = getDate<number>(123); console.log(val);
tips
这里的T 能够改为其余任意值,但定义的值和传入的参数以及返回的值是同样的。通常默认写法是T,也是业内规范的选择。
class MainClass<T> { public list: T[] = []; add(value: T): void { this.list.push(value); } min(): T { let minNum = this.list[0]; for(let i = 0; i < this.list.length; i++) { minNum < this.list[i] ? minNum : this.list[i]; } return minNum; } } // 实例化类,指定类的T的类型是number const minClass = new MainClass<number>(); minClass.add(1); minClass.add(2); minClass.add(3); console.log(minClass.min()); // 实例化类,并指定了类的T的类型是string,则其方法的传参和返回值都是string类型 let minClass2 = new MainClass<string>(); minClass2.add('1'); minClass2.add('2'); minClass2.add('3'); console.log(minClass2.min());
interface Config { // 规范参数类型和返回值类型 <T>(value: T): T; } let getDate: Config = function <T>(value: T): T { return value; } const data = getData<string>('123'); console.log(data);
interface Config<T> { // 规范参数和返回值类型 (value: T): T } // 接口方法 function getData<T>(value: T): T { return value; } // 使用接口 let myGetData: Config<string> = getData; consoie.log(myGetData('123'));
tips
接口的泛型只针对函数类型的接口
class User { username: string | undefined; password: string | undefined; constructor(params: { usermame: string | undefined, password?: string | undefined }) { this.username = params.username; this.password = params.password; } } class Db<T> { add(user: T): boolean { console.log(user); return true; } updated(user: T, id: number): boolean { console.log(user, id); return true; } } let user = new User({ username: 'Clearlove' }); user.password = '123'; let db = new Db<User>(); db.add(user); db.updated(user, 1);
tips
类的参数名和类型都作了约束
内部模块成为命名空间,外部模块简称为模块,模块在起自身的做用域里执行,而不是在全局做用域。
定义在一个模块里的变量、函数、类等在模块外是不可见的,除非你明确的使用export
形式导出它们。
对应的,若是想使用其余模块导出的变量、函数、类等,须要导入它们,可使用import
。
// modules/db.ts function getData(): any[] { console.log('获取数据'); return [ { userName: '张三' }, { userName: '李四 } ]; } // 一个模块可使用屡次 export { getData }; // 一个模块只能使用一次 export default getData;
import { getData as getDbData } from './modules/db'; import getDbData from './modules/db'; getDbData();
tips
浏览器中不能直接使用,可在node
和webpack
的环境中调试
在代码量较大的状况下,为了不各类变量命名冲突,可将类似功能的函数、类、接口等放置到命名空间内。TypeScript的命名空间能够将代码包裹起来,只对外部暴露须要在外部访问的对象。
命名空间和模块的区别:
// modules/Animal.ts export namespace A { interface Animal { name: string; say(): void; } export class Dog implements Animal { name: string; constructor(name: string) { this.name = name; } say() { console.log(`我是${this.name}`); } } } export namespace B { interface Animal { name: string; eat(): void; } export class Dog implements Animal { name: string; constructor(name: string) { this.name = name; } say() { console.log(`Hello, my name is ${this.name}`); } } }
import {A, B} from './modules/Animal'; const dog = new A.Dog('小马'); dog.say();
装饰器本质上是一种特殊的函数,被应用在于:
因此应用装饰器其实很想是组合一系列函数,相似于高阶函数和类。
装饰器的语法十分简单,只须要在想使用的装饰器前面加上@
符号,装饰器就会被应用到目标上:
function simpleDecorator() { console.log('i am a decorator!'); } @simpleDecorator class A {}
一共有5种装饰器能够被咱们使用:
@classDecorator class Bird { // 属性装饰器 @propertyDecorator name: string; // 方法装饰器 @methodDecorator fly ( // 参数装饰器 @parameterDecorator meters: number ) {} // 访问器装饰器 @accessorDecorator get egg() {} }
装饰器只在解析执行时应用一次,例如:
function f() { console.log('apply decotator'); return true; } @f class A {} // output: apply decorator
这里的代码会在终端中打印apply decorator
,即便咱们其实并无使用类A
不一样类型的装饰器执行顺序是明肯定义的:
例如:
function f(key: string) { console.log(`evaluate: ${key}`); return function() { console.log(`call: ${key}`); } } @f('class Decorator') class A { @f('Static Property') static prop?: number; @f('Static Method') static method(@f('Static Methos Parameter') foo) {} constructor(@f('Constructor Parameter') foo) {} @f('Instance Method') method(@f('Instance Mthdos Parameter') foo) {} @f('Instance Propterty') prop?: number; } // evaluate Inastance Method // evaluate Inastance Method Parameter // call: Instace Method Parameter // call: Instace Method // evaluate Inastance Property // call: Inastance Property // evaluate Static Property // call: Static Property // evaluate Static Method // evaluate Static Method Parameter // call: Static Method Parameter // call: Static Method // evaluate: Class Decorator // evaluate: Constructor Decorator // call: Constructor Decorator // call: Class Decorator
你也许会注意到,执行实例属性prop
晚于实例方法method
。然而执行静态属性static prop
早于静态方法static method
。
这是由于对于属性/方法/访问器 装饰器而言,执行顺序取决于它们的声明顺序。
然而,同一方法中不一样参数的装饰器的执行顺序是相反的,最后一个参数的装饰器会被先执行:
function f(key: string) { console.log(`evaluate: ${key}`); return function () { console.log(`call: ${key}`); } } class A { method() { @f('Parameter Foo') foo, @f('Parameter Bar') bar } {} } // evaluate Parameter Foo // evaluate Parameter Bar // call Parameter Bar // call Parameter Foo
能够对同一个目标应用多个装饰器,它们的组合顺序为:
例如:
function f(key: string) { console.log(`evaluate: ${key}`); return function () { console.log(`call: ${key}`); } } class A { @f('Outer Method') @f('Inner Method') method() {} } // evaluate: Outer Method // evaluate: Inner Method // call: Inner Method // call: Outer Method
应用于类构造器,其参数类的构造函数
function addAge(args: number) { return function(target: Function) { target.prototype.age = args; } } @addAge(18) class Hello { name: string; age: number; constructor() { console.log('Hello World'); this.name = 'Clearlove'; } } console.log(Hello.prototype.age); // 18 const hello = new Hello(); console.log(hello.age); // 18
它会被应用到方法的属性描述符上,能够用来监视、修改或者替换方法定义。
方法装饰器会在运行时传入下列三个参数:
function addAge(constructor: Function) { constructor.prototype.age = 18; } function method(tagrget: any, propertyKey: string, descriptor: PropertyDescriptor) { console.log(target); console.log(`prop: ${propertyKey}`); console.log(`desc: ${JSON.stringify(descriptor)}` + '\n\n'); } @addAge class Hello { name: string; age: number; constructor() { console.log('Hello World'); this.name = 'Clearlove'; } @method hello(): string { return 'Instance method'; } @method static sayHello(): string { return 'Static method'; } } // Hello {hello: [Function]} // prop: hello // desc: {"writabel": true, "enumerabel": true, "configurable": true} // { [Function: Hello] seyHello: [Function] } // prop: sayHello /// desc: {"writabel": true, "enumerabel": true, "configurable": true}
假如修饰的是hello
这个实例方法,第一个参数就是原型对象,也就是Hello.prototype。
加入修饰的是sayHello
这个静态方法,第一个参数就是构造器constructor
。
访问器装饰器应用于访问器的属性描述符,可用于观察、修改或替换访问者的定义。
访问器不能再声明文件中使用,也不能在任何其余环境上下文中使用(例如在声明类中)。
访问器装饰器表达式会在运行时看成函数被调用,传人下列三个参数:
例子:
function configurabele(value: boolean) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { descriptor.configurabel = value; } } class Point { private _x: number; private _y: number; constructor(x: number, y: number) { this._x = x; this._y = y; } @configurable(false) get _x() { return this_.x; } @configurable(false) get _y() { return this._y; } }
属性装饰器表达式会在运行时看成函数被调用,传人下列2个参数:
function log(target: any, propertyKey: string) { let value = target[properttKey]; const getter = function () { console.log(`Getter for ${propertyKey} returned ${value}`); return value; } const setter = function (newVal) { console.log(`Set ${propertyKey} to ${newVa;}`); value = newVal; } if (delete this[propertyKey]) { Object.defineProperty(target, propertyKey, { get: getter, set: setter, enumerable: true, configurable: true }); } } class Calculator { @log public num: number; square() { this.num * this.num; } } const cal = new Calculator(); cal.num = 2; console.log(cal.square); // Set num to 2 // Getter for num returned 2 // Getter for num returned 2 // 4
参数装饰器表达式会在运行时看成函数被调用,传入下列3个参数:
const parseConf = []; // 在函数调用前执行格式化操做 function parseFunc(target: any, name, descriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args: any[]) { for (let index = 0; index < parseConf.length; index++) { const type = parseConf[index]; console.log(type); switch (type) { case 'number': args[index] = Number(args[index]); break; case 'string': args[index] = String(args[index]); break; case 'boolean': args[index] = String(args[index]) === 'true'; break; } return originalMethod.apply(this, args); } }; return descriptor; } // 向全局对象中添加对应的格式化信息 function parse(type) { return function (target, name, index) { parseConf[index] = type; console.log('parseConf[index]:', type); }; } class Modal { @parseFunc public addOne(@parse('number') num) { console.log('num:', num); return num + 1; } } let modal = new Modal(); console.log(modal.addOne('10')); // 11