# TypeScript-基础类型
string
,number
, boolean
,数组,元组, any 就不展开了, 没什么好说的。
# 枚举
enum Color {
RED = 'RED',
GREEN = 'GREEN',
BLUE = 'BLUE'
}
Color.RED === 'RED'
默认情况下,从0
开始为元素编号。 你也可以手动的指定成员的数值。可以指定数值或者其他的类型, 比如字符串
# void
void
大部分情况下都用于声明函数没有返回值
function foo(): void {}
interface Module {
foo(): void;
}
在js中, 没有返回值的函数会默认返回undefined
void和undefined不同
# null、undefined
他们的本身类型也叫null 和 undefined。 可以配置 strictNullChecks
选项来严格检查空值
let v: number | null = null;
默认情况下,类型检查器认为 null
与 undefined
可以赋值给任何类型。 null
与 undefined
是所有其它类型的一个有效值。--strictNullChecks
标记可以解决此错误:当你声明一个变量时,它不会自动地包含 null
或 undefined
。
{
"compilerOptions": {
"strictNullChecks": true
}
}
你可以使用联合类型明确的包含它们:
let s: string | null = '22'
s = null; // 合法
使用了 --strictNullChecks
,可选参数会被自动地加上 | undefined
:
function foo(a: number, b?: number) {}
foo(1);
foo(1, 2);
foo(1, undefined);
// 类型“null”的参数不能赋给类型“number | undefined”的参数.
foo(1, null);
可选属性也是一样的处理
class C {
a: string;
b?: number;
}
const c = new C()
// (property) C.b?: number | undefined
c.b
# never
never
类型表示的是那些永不存在的值的类型. 个人确实很少用的高
# object
object
表示非原始类型,也就是除number
,string
,boolean
,symbol
,null
或undefined
之外的类型。
let obj: object = {}
object 不同于
Object
,总是用object
!
WARNING
注意在 JavaScript 中,函数就是对象,他们可以有属性,在他们的原型链上有 Object.prototype
,并且 instanceof Object
。你可以对函数使用 Object.keys
等等。由于这些原因,在 TypeScript 中,函数也被认为是 object
。
# unknow
unknown
类型可以表示任何值。有点类似于 any
,但是更安全,因为对 unknown
类型的值做任何事情都是不合法的:
function foo(a: unknown) {
console.log(a);
// 警告:对象的类型为 "unknown"。
a.b()
}
any
就是可以做任何的事情
# 类型断言
采用尖括号或者 as 语法,开发者手动指定变量的类型
let str: any = 'asdasd'
const len = <string>str.length
const len2 = (str as string).length
# 接口interface
这个需要注意下只读和可选属性即可
interface Person {
name: string;
age: number;
sex?: 1 | 2;
readonly height: number;
}
字符串索引签名, 有时候接口可能存在一些没有列举的属性, 这时候可以使用这个
interface Person {
name: string;
age: number;
sex?: 1 | 2;
readonly height: number;
[key: string]: any
}
# 类 类型
可以采用implements 表示类的实现
interface AnimalInterface {
name: string;
setName(name: string): void;
}
class AnimalClass implements AnimalInterface {
public name: string;
setName(name: string): void {
this.name = name;
}
}
使用new声明一个构造函数
interface PersonConstructor {
new (name: string, age: number);
}
class PersonClass {
public name: string;
public age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
class Animal2Class {
public name: string;
public age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
function createPerson(ctor: PersonConstructor, name: string, age: number) {
return new ctor(name, age);
}
createPerson(PersonClass, 'xdyuan', 30);
createPerson(Animal2Class, 'cat', 3);
构造函数签名可能更多的可以用于声明一些构造函数参数
也可以声明一个类型, 既可以使用new也可以直接调用, 比如Date
interface Date2 {
new (): Date2;
new (
year: number,
month: number,
date?: number,
hours?: number,
minutes?: number,
seconds?: number,
ms?: number
): Date2;
(s: number): number;
UTC(
year: number,
month: number,
date?: number,
hours?: number,
minutes?: number,
seconds?: number,
ms?: number
): number;
now(): number;
}
# 接口继承
export namespace TS {
export interface A {
a: string;
}
export interface B {
b: number;
}
export interface C extends A, B {
c: boolean;
}
}
# 混合类型
// 混合类型
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
const counter = <Counter>function (s: number) {
return s.toString();
};
counter.interval = 100;
counter.reset = function () {
this.interval = 0;
};
return counter;
}
let c = getCounter();
c(100);
Counter 声明一个函数, 函数作为对象拥有属性interval和reset
# 接口继承类
当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。
这个是实际用到的少, 可能继承用的比较少
# 类
这个也没啥好说的, 就是需要注意一些实例属性的各种修饰符public、protected、 private。还有就是类的继承
# readonly修饰符
你可以使用 readonly
关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
class ClassA {
readonly name: string;
constructor() {
this.name = '1';
}
}
// 无法分配到 "name" ,因为它是只读属性。
new ClassA().name = '1'
# 参数属性
这种其实我不建议用,增加心智负担,还是声明属性直白些。
# 静态属性
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
class Animal {
public static className: string = 'Animal';
public static print() {
console.log(Animal.className);
}
}
# 抽象类
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
如果"strictPropertyInitialization": true
, 表明类成员属性必须初始化, 可以在构造函数初始化, 如果需要在其他地方初始化, 比如set函数, 可以采用 感叹号语法
class Point {
public x: number = 0;
public y!: number;
public z: number;
constructor(z: number) {
this.z = z;
}
}
这些其实大概知道就行, 学过java的就和那个基本一样的, 注意关键字abstract 即可。
# 函数
函数也没什么好说的, 就是需要知道参数类型和返回值类型 ,参数默认值和参数是否可选, 剩余参数等等。
使用接口声明一个函数类型 注意看这个是没有函数名称的
interface SearchFunc {
(source: string, target: string): boolean;
}
const searchFunc: SearchFunc = (s: string, t: string) => s.indexOf(t) > -1;
也可以使用类型别名声明一个函数
type SearchFunc2 = (source: string, target: string) => boolean;
const searchFunc2: SearchFunc2 = (s, t) => s.indexOf(t) > -1;
如果是声明一个对象具有某个函数属性, 可能就需要函数Function.property
interface Person {
property: string;
sayHi(msg: string): void
}
# this
interface Deck {
name: string;
age: number;
sayName(): void;
}
let deck: Deck = {
name: 'deck',
age: 100,
sayName() {
console.log(this.name);
}
};
deck.sayName();
看上面的代码, 虽然不会报错, 但是this会被推断为any类型。我们可以显示的给this指定类型
有两种方式, 我觉得使用断言 as也是可以的, 或者是在参数上声明
interface Deck {
name: string;
sayName(msg: string): void;
sayHi(): void;
}
let deck: Deck = {
name: 'deck',
sayName(this: Deck, msg: string) {
console.log(this.name, msg);
},
sayHi() {
const _this = <Deck>this;
console.log(_this.name);
}
};
deck.sayName('hello');
实在搞不定的, 直接使用bind函数
# 多态的this类型
下面是一个返回this链式调用的例子。 可以设置方法的返回值为this,这样子add方法也不会返回BasicCalc类型, 而是会返回ScientificCalc。 否则返回BasicCalc类型的话, 就找不到sin方法了。理论上看起来是错的。
class BasicCalc {
public value: number;
constructor(value: number) {
this.value = value;
}
public currentValue(): number {
return this.value;
}
public add(v: number): this {
this.value += v;
return this;
}
public times(v: number): this {
this.value *= v;
return this;
}
}
class ScientificCalc extends BasicCalc {
constructor(value: number) {
super(value);
}
public sin(): this {
this.value = Math.sin(this.value);
return this;
}
}
const v = new ScientificCalc(0).add(1).times(10).sin().currentValue();
console.log(v);
# 函数重载
JavaScript本身是个动态语言。 JavaScript里函数根据传入不同的参数而返回不同类型的数据是很常见的。
function returnType(str: string): string;
function returnType(num: number): string;
function returnType(x) {
return typeof x;
}
console.log(returnType('11'));
console.log(returnType(11));
// 报警告 针对此实现的调用已成功,但重载的实现签名在外部不可见
console.log(returnType(true));
写进函数体的签名是对外部来说是“不可见”的,这也就意味着外界“看不到”它的签名,自然不能按照实现签名的方式来调用。实现签名对外界来说是不可见的。当写一个重载函数的时候,你应该总是需要来两个或者更多的签名在实现签名之上。而且实现签名必须和重载签名必须兼容(compatible),举个例子,这些函数之所以报错就是因为它们的实现签名并没有正确的和重载签名匹配。
function fn(x: boolean): void;
// 此重载签名与其实现签名不兼容
function fn(x: string): void;
// This overload signature is not compatible with its implementation signature.
function fn(x: boolean) {}
正确的实现
function fn(x: string): string;
// Return type isn't right
function fn(x: number): boolean;
This overload signature is not compatible with its implementation signature.
function fn(x: string | number) {
return "oops";
}
TIP
尽可能的使用联合类型替代重载
重载的功能很多时候可以被泛型给代替, 所以我们能使用泛型的地方可以尽量使用泛型。
# 泛型简介
使用泛型
来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件
function identify<T>(x: T): T {
return x;
}
// 用法
const ret = identify<number>(100)
// 或者 类型会自动推论
const ret = identify(100)
// 类型别名也是可以的
type foo = <T>(arg: T) => T;
const myIdentify: foo = identify;
这就是一个典型的泛型函数。
# 泛型接口
// 声明一个函数的泛型接口
interface GenericIdentify<T> {
(arg: T): T;
}
const identify2: GenericIdentify<number> = a => a;
# 泛型类
class Person<T> {
public name: T;
getName(): T {
return this.name;
}
}
# 泛型约束
interface LengthWise {
length: number;
}
function getLength<T extends LengthWise>(arg: T) {
return arg.length;
}
现在要求传入的泛型必须具有length属性, 如果传入一个数字就会报错。 下面是另外的例子
function getProppertyValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const obj = { a: '1', b: '2' };
getProppertyValue(obj, 'a');
// 警告: 类型“"c"”的参数不能赋给类型“"a" | "b"”的参数。
getProppertyValue(obj, 'c');