根本数据类型
数字
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 = {};
undefined
罕用于组合类型
const h: number | undefined;
null
const i: null;
元组
可为数组中的每个参数定义绝对应的类型
const j: [number, string] = [1, '2'];
枚举
enum err {
first = 3,
'second',
}
const k: e = err.first;
console.log(g); // 4
tips
- 如果 未赋值 的上一个值是 数字 ,那么这个未赋值的值就是 上一个值 +1
- 如果 未赋值 的上一个值 未赋值 ,那么输入的就是 它的下标
- 如果 未赋值 的上一个值是 非数字 ,那么 必须赋值
void
指定办法类型,示意没有返回值,办法体中不能有return
function add(): void {console.log('add');
}
// 如果办法体有返回值,能够加上返回值的类型
function delete: string {return 'delete';}
never
其余类型 (包含 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
tips
- public 在以后类、子类和类以外都能够拜访
- protected 在以后类、子类外部都能够拜访,类内部无法访问
- private 在以后类外部能够拜访,子类和类内部无法访问
- 属性不加修饰符,默认都是 public
多态
通过形象办法 / 办法重载,实现多态。多态的作用是用来定义规范
// 形象父类
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 的命名空间能够将代码包裹起来,只对外部裸露须要在内部拜访的对象。
命名空间和模块的区别:
- 命名空间: 外部模块,次要用于组织代码,防止命名抵触
- 模块: ts 内部模块的简称,偏重代码的复用,一个模块里可能会有多个命名空间
// 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
办法装璜器
它会被利用到办法的属性描述符上,能够用来监督、批改或者替换办法定义。
办法装璜器会在运行时传入下列三个参数:
- 对于动态成员来: 类的构造函数,对于实例成员: 类的原型对象
- 成员的名字
- 成员的属性描述符(value: any, writable: boolean, enumerable: boolean, configurabel: boolean)
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