本文是我的学习TypeScript中的我的总结, 其中主要是一些重要经常使用的点, 还有一些不太好理解的问题, 同时也参考了一些网上文章, 但愿能够帮助初学者快速入门,若有不对之处, 还望指出,感谢~html
联合类型
表示一个值能够是几种类型之一, 至关于集合交集. 竖线(|)分隔; 若一个值是联合类型,只能访问此联合类型的全部类型里共有的成员java
交叉类型
将多个类型合并为一个类型, 至关于集合并集. 竖线(&)分隔;把现有的多种类型叠加到一块儿成为一种类型,它包含了所需的全部类型的特性,多用于mixinsnode
映射类型:从旧类型建立新类型的一种方式 (包含Readonly Partial Record Pick)react
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
type Partial<T> = {
[P in keyof T]?: T[P];
}
interface Person {
name: string;
age: number;
}
// 调用
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
复制代码
// 结果
type PersonPartial = {
name?: string|undefined;
age?: number|undefined;
}
type PersonReadonly {
readonly name: string;
readonly age: number;
}
复制代码
用户自定义类型保护:定义函数,返回类型谓词(语法:parameterName is Type)
git
typeof类型保护:只有两种形式能被识别typeof v === "typename"
和 typeof v !== "typename"
, "typename"必须是 "number", "string", "boolean"或 "symbol" 原始类型,若为除前四个的其余字符串,将不被识别为类型保护es6
instanceof 类型保护:过构造函数来细化类型的一种方式。语法: 实例 instanceof 构造函数
typescript
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Jarid',
age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, ['Jarid]
复制代码
keyof T
, 索引类型查询操做符 (此实例中,为person的属性name、age,值等于'name'|'age')
K extends keyof T
,泛型约束
T[K]
, 索引访问操做符(此实例中,为person['name'])express
小结:(如下图来自于网络, 感谢~) 编程
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
类型变量(类型参数) 通常用T
表示json
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
复制代码
使用含有泛型的接口来定义函数形状
interface ConfigFn{
<T>(value:T):T;
}
var getData:ConfigFn=function<T>(value:T):T{
return value;
}
getData<string>('张三');
getData<string>(1243); //错误
复制代码
// 把类型参数提到接口名上
// 写法一:
interface ConfigFn<T>{
(value:T):T;
}
var getData:ConfigFn<string>=function<T>(value:T):T{
return value;
}
getData('20'); /*正确*/
// 写法二:
interface ConfigFn<T>{
(value:T):T;
}
function getData<T>(value:T):T{
return value;
}
var myGetData:ConfigFn<string>=getData;
myGetData('20'); /*正确*/
myGetData(20) //错误
复制代码
使用泛型来定义类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
复制代码
一、抽象类是提供其余类继承的基类,不能直接被实例化.
二、用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现而且必须在派生类中实现.
三、abstract抽象方法只能放在抽象类里面.
四、可使用修饰符.
五、abstract 修饰,里面能够没有抽象方法。但有抽象方法(abstract method)的类必须声明为抽象类(abstract class)
复制代码
注意
:
1.使用多态基础是类的继承或者接口实现。
2.若是子类继承的是一个抽象类,子类必须实现父类里的抽象方法,否则的话不能实例化,会报错。
3.多态的子类继承父类,子类能够不实现该方法,但使用多态就是子类为了实现方法
复制代码
class Greeter {
static standardGreeting = "Hello, there"; // 静态属性
greeting: string;
greet() {
if (this.greeting) {
return "Hello, " + this.greeting;
} else {
return Greeter.standardGreeting;
}
}
}
// typeof Greeter: 构造函数的类型, 包含类的静态成员和构造函数
let greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
// Greeter: 实例类型
let greeter2: Greeter = new greeterMaker();
console.log(greeter2.greet());
复制代码
在面向对象的编程中,接口是一种规范的定义,它定义了行为和动做的规范; 接口定义了某一批类所须要遵照的规范,不关心这些类的内部状态数据和其中方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就能够知足实际须要.
interface Person {
readonly id: number; // 只读属性
name: string; // 肯定属性
age?: number; // 可选属性
[propName: string]: string; // 字符串索引
[index: number]: string; // 数字索引,相似数组
(name: string, age: number): void; // 函数
getName(id: number): string; // 方法
new(name: string, age: number): Person; // 构造函数
}
复制代码
注
:
1.上面只是展现接口支持的功能, 不表明能够所有写在一块儿.
2.若数字索引和字符串索引同时存在, 数字索引的返回值必须是字符串索引返回值类型的子类型. 由于当使用 number来索引时,JavaScript会将它转换成string而后再去索引对象
3.字符串索引与其余属性同时存在, 其余属性值类型必须是字符串索引返回值类型的子类型. 由于字符串索引声明了 obj.property和obj[“property”]两种形式均可以
类实现接口, 用接口来明确的强制一个类去符合某种契约
// 接口描述类的实例部分
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { } // 类静态部分
}
复制代码
类分为静态和实例部分:
当你用构造器签名去定义一个接口并试图定义一个类去实现这个接口时会获得一个错误, 由于当一个类实现了一个接口时,只对其实例部分进行类型检查。 constructor存在于类的静态部分,因此不在检查的范围内
在实现接口时, 若想对构造函数和实例部分作约束, 能够分别约束, 并经过一个新的构造函数(以下:createClock)生成实例
// ClockConstructor 为构造函数所用
// ClockInterface 为实例方法所用
// createClock的第一个参数是ClockConstructor类型,在createClock(AnalogClock, 7, 32)里,会检查AnalogClock是否符合构造函数签名。
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick();
}
// 第一个参数ctor的类型是接口 ClockConstructor,在这里就为类的静态部分指定须要实现的接口
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute);
}
// 类 DigitalClock 实例化出来的对象(类的实例部分)应该知足这个接口的规则
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("tick tock");
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
复制代码
// 函数接口
interface Counter {
(start: number): string
}
let counter: Counter
counter = function (start: number) {
console.log(number)
}
// 调用
counter(12)
复制代码
// 对象接口
interface Counter {
interval: number;
reset(): void;
}
复制代码
下面的官方例子, 混合类型等同于上面两个接口声明合并
// 一个对象能够同时作为函数和对象使用,并带有额外的属性。如:下文中的变量c
interface Counter {
(start: number): string; // 函数
interval: number; // 对象属性
reset(): void; // 对象方法
}
function getCounter(): Counter {
// 经过类型断言,将函数对象转换为Counter类型,转换后的对象不但实现了函数接口的描述,使之成为一个函数,还具备interval属性和reset()方法
let counter = <Counter>function (start: number) { console.log(number) };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
复制代码
混合类型主要运用于相似UMD库, 如老朋友jQuery, 既能够做为函数,也能够做为对象使用, 最近看Vue3的源码, 发现里面也存在, 以下:
export interface ReactiveEffect<T = any> {
(): T
_isEffect: true
active: boolean
raw: () => T
deps: Array<Dep>
options: ReactiveEffectOptions
}
function createReactiveEffect<T = any>(
fn: () => T,
options: ReactiveEffectOptions
): ReactiveEffect<T> {
const effect = function reactiveEffect(...args: unknown[]): unknown {
return run(effect, fn, args)
} as ReactiveEffect
effect._isEffect = true
effect.active = true
effect.raw = fn
effect.deps = []
effect.options = options
return effect
}
复制代码
ps
: 此处主要讲下,接口继承类, 其余你们应该理解, 或可自行查阅文档
接口继承类时,会继承类的全部成员但不包含实现
class Person {
type: string // ️这里是类的描述
}
interface Child extends Person { // ️Child 接口继承自 Person 类,所以规范了 type 属性
log(): void
// 这里其实有一个 type: string
}
// 上面的 Child 接口继承了 Person 对 type 的描述,还定义了 Child 接口自己 log 的描述
// 第一种写法
class Girl implements Child {
type: 'child' // 接口继承自 Person 的
log() {} // 接口自己规范的
}
// 第二种写法
class Boy extends Person implements Child { // 首先 extends 了 Person 类,而后还需知足 Child 接口的描述
type: 'child'
log() {}
}
复制代码
当接口继承了一个拥有私有(private)或受保护(protected)的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。
class Person {
private type: string // ️这里是类的描述
}
interface Child extends Person { // ️Child 接口继承自 Person 类,所以规范了 type 属性
log(): void
// 这里其实有一个 type: string
}
// 上面的 Child 接口继承了 Person 对 type 的描述,还定义了 Child 接口自己 log 的描述
// 写法 (only)
class Boy extends Person implements Child { // 首先 extends 了 Person 类,而后还需知足 Child 接口的描述
type: 'child'
log() {}
}
复制代码
总的来讲,这种模式是应当避免的(不推荐),尤为是在类拥有私有成员时.
一、函数声明
function sum(x: number, y: number): number {
return x + y;
}
复制代码
二、函数表达式
let mySum = function (x: number, y: number): number {
return x + y;
};
// 或者
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
复制代码
注意:
1.可选参数(?:): 必须接在必需参数后面
2.参数默认值: 至关于可选参数,任意位置
3.剩余参数(...rest): 必须是在最后,与 es6 相同
java中的重载:同名函数,参数不同. 容许一个函数接受不一样数量或类型的参数时,做出不一样的处理. typescript中的重载:经过为同一个函数提供多个函数类型定义,一个函数体实现多种功能的目的. ts为了兼容es5 以及 es6 重载的写法和java中有区别.
function reverse(x: number): number; // 函数定义
function reverse(x: string): string; // 函数定义
function reverse(x: number | string): number | string { // 函数实现
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
复制代码
注意:
1.返回结果不一样时须要使用重载, 仅是参数不一样,可使用联合类型
2.此外, 重载的顺序应该是匹配范围从小到大, 由于重载匹配是从上到下, 匹配后再也不往下查找
复制代码
指定编译选项和项目待编译文件, 主要包含如下选项:
TS 文件指拓展名为 .ts、.tsx 或 .d.ts 的文件。若是开启了 compilerOptions.allowJs 选项,那 .js 和 .jsx 文件也属于 TS 文件
1. 若files 和 include 都未设置,那么除了 exclude 排除的文件,编译器会默认包含路径下的全部 TS 文件。
2. 若同时设置 files 和 include ,那么编译器会把二者指定的文件都引入
3. 若未设置 exclude ,那其默认值为 node_modules 、bower_components、jspm_packages 和编译选项 outDir 指定的路径
4. exclude 只对 include 有效,对 files 无效
5. {
"compilerOptions": {
"typeRoots" : ["./typings"], // typeRoots指定的目录下的包, 会被编译, 即指定被编译包所在目录
"types": ["node", "lodash", "express"] // 指定被编译的包
}
}
复制代码
--target: 指定编译后的js版本
--lib: 编译中包含的库
// 其余参考官方文档
复制代码
须要注意的点
:
1.不带任何输入文件的状况下调用tsc,编译器会从当前目录开始去查找tsconfig.json文件,逐级向上搜索父目录。
2.不带任何输入文件的状况下调用tsc,且使用命令行参数--project(或-p)指定一个包含tsconfig.json文件的目录。
3.当命令行上指定了输入文件时,tsconfig.json文件会被忽略, 按照默认选项编译该文件
复制代码
定义类型有两种方式: 接口(interface) 和类型别名(type alias)
一、interface 只能定义对象类型或者函数, type 还能够定义组合类型,交叉类型(&,相似并集),联合类型(|,相似交集),原始类型
二、interface 方式能够实现接口的 extends 和 implements , 而 type alias 则不行。
三、interface 能够实现接口的合并,但 type alias 则不行。
复制代码
TypeScript 3.0 引入了新的unknown 类型,它是 any 类型对应的安全类型。
unknown: 在对 unknown 类型的值执行大多数操做以前,必须进行某种形式的检查。
any: 在对 any 类型的值执行操做以前,没必要进行任何检查。
复制代码
关于unknown可参考 juejin.im/post/5d04ac…
二者定义或修饰的值, 不能更改, 只读
const: 用来定义(变)常量
readonly: 用来修饰对象属性
复制代码
我以为学习一门语言,在熟悉文档概念后, 最终应该在实践加深体会,多踩踩坑,才能更好的掌握,毕竟学习的目的就是为了更好的为工做业务服务, 如今TS已经比较成熟了,你们确定都比较熟,若还未上车,能够开始了.
以后有时间在总结下本身开发中遇到的问题, 感谢~~~ 关于文中很差不妥之处,还请多多包含并指出,第一次在掘金写,手动捂脸!
www.typescriptlang.org/ www.tslang.cn/ ts.xcatliu.com/advanced/cl…
www.softwhy.com/article-860…
blog.poetries.top/2019/09/03/…
kbscript.com/2017/01/27/…
juejin.im/post/5c2723…