常见的定义
const b: boolean = false;
const n: number = 1;
const s: string = 'abc';
const n: null = null;
const u: undefined = undefined;
// 声明变量空值 只能复制undefined或者null
const v: void = undefined || null;
const union: string | number = 7;
// 当访问联合类型的属性或方法时 只能访问共有属性和方法
union.toString() // toString 共有
readonly id:number; // 只读属性 只有在创建的时候可以赋值
空值返回函数
function func(): void {}
空值和undefined&null的区别是 void类型变量只能赋值undefined&null 但是声明为undefined&null的 可以和其他原始数据类型相互赋对应类型的值
任意值 any
const s; // s是任意值 不受类型约束
const s = 7; // 推断为number 受number约束
接口声明
interface Person {
name: string;
age?: number; // 可选
love: any; // 任意类型
[propName: string]: string // 任意属性 如果用了任意属性 则其他成员属性必须是该任意属性类型的子类型
// 此时任意属性类型是string, 所以name,age,love都应该为string或者undeined或者null
[propName: string]: string | number | any // 这样就可以解决上面必须是它子类型的问题
}
数组
const arr: number[] = [1, 2, 3] // 元素类型为number的数组
const arr: Array<number> = [1, 2, 3]; // 泛型表示跟上面一条相等
// 接口表示数组也可以
interface NumberArray {
[index: number]: number
}
// 类数组处理
function sum() {
let args: number[] = arguments; // 报错 arguments 不是数组
}
// 可以用接口表示 也可以用内置定义 IArguments 实现也相同
function sum() {
const args: {
[index: number]: any;
length: number;
callee: Function;
}
}
函数表达式
函数是javascript的一等公民
// 函数声明
function sum(x: number, y: number): number {
return x + y;
}
// 函数表达式
const mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
// 用接口定义函数
interface SearchFunc {
(source: string, subString: string): boolean;
}
const mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
// 可选参数 可选参数后面不允许出现必须参数
function func(name: string, age?: number)
// 参数默认值
function func(name: string = 'abc', age?: number)
// es6的剩余参数 ...rest参数
function push(array: any[], ...items: any[]) {
array.push()...
}
push([], 1, 2, 3, 4)
// 重载
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('');
}
}
类型断言(表达式)
欺骗typescript编译器 尽量避免 类型断言并不会实质修改变量类型 只是ts编译过程使用
// 语法
值 as 类型 // animal as Cat 类型降级 动物降级成猫
<类型>值 // <Cat>animal 类型降级 动物降级成猫
// animal as Fish
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
// 联合类型
function isFish(animal: Cat | Fish) {
if (typeof (animal as Fish).swim === 'function') {
return true;
}
return false;
}
// 联合类型可以被断言为其中一个类型
// 父类可以被断言为子类 继承
// 任何类型都可以被断言为 any
// any 可以被断言为任何类型
// 若 A 兼容 B,那么 A 能够被断言为 B,B 也能被断言为 A 要使得 A 能够被断言为 B,只需要 A 兼容 B 或 B 兼容 A 即可
// 要使得 A 能够被断言为 B,只需要 A 兼容 B 或 B 兼容 A 即可
双重断言
// 任何类型都可以被断言为 any
// any 可以被断言为任何类型
interface Cat {
run(): void;
}
interface Fish {
swim(): void;
}
function testCat(cat: Cat) {
return (cat as any as Fish); // 牛逼
}
声明文件
// 全局变量
declare let jQuery: (selector: string) => any;
declare var jQuery: (selector: string) => any;
declare const jQuery: (selector: string) => any;
// 全局方法
declare function jQuery(selector: string): any;
// 全局方法重载
declare function jQuery(selector: string): any;
declare function jQuery(domReadyCallback: () => any): any;
// 全局类
declare class Animal {
name: string;
constructor(name: string);
sayHi(): string;
}
// 定义全局枚举
declare enum Directions {
Up,
Down,
Left,
Right
}
// 定义全局变量对象
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
}
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
const version: number;
class Event {
blur(eventType: EventType): void
}
enum EventType {
CustomClick
}
}
// 命名空间嵌套
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
namespace fn {
function extend(object: any): void;
}
}
/ ************ /
jQuery.ajax('/api/get_something');
jQuery.fn.extend({
check: function() {
return this.each(function() {
this.checked = true;
});
}
});
// 单属性空间嵌套
declare namespace jQuery.fn {
function extend(object: any): void;
}
/ ****** /
jQuery.fn.extend({
check: function() {
return this.each(function() {
this.checked = true;
});
}
});
// interface和type组合暴露两者
// 声明
interface AjaxSettings {
method?: 'GET' | 'POST'
data?: any;
}
declare namespace jQuery {
function ajax(url: string, settings?: AjaxSettings): void;
}
// 使用
let settings: AjaxSettings = {
method: 'POST',
data: {
name: 'foo'
}
};
jQuery.ajax('/api/post_something', settings);
// 未避免命名冲突
declare namespace jQuery {
interface AjaxSettings {
method?: 'GET' | 'POST'
data?: any;
}
function ajax(url: string, settings?: AjaxSettings): void;
}
let settings: jQuery.AjaxSettings = {
method: 'POST',
data: {
name: 'foo'
}
};
jQuery.ajax('/api/post_something', settings);
类型别名
// 类型别名
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
字符串字面量类型
// 限定了只能是这三类中的一类
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
// do something
}
handleEvent(document.getElementById('hello'), 'scroll'); // 没问题
handleEvent(document.getElementById('world'), 'dblclick'); // 报错,event 不能为 'dblclick'
// index.ts(7,47): error TS2345: Argument of type '"dblclick"' is not assignable to parameter of type 'EventNames'.
枚举
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; // 0,2,3,4,5,6...
// 手动赋值 后续会在赋值后的值上累加
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu=99, Fri, Sat}; // Tue = 2, Fri = 100
extends
// TS的类型也支持条件运算,其语法与三目运算符相同,为T extends U ? X : Y
type IsEqualType<A, B> = A extends B ? (B extends A ? true : false) : false;
type NumberEqualsToString = IsEqualType<number, string>; // false
type NumberEqualsToNumber = IsEqualType<number, number>; // true
// redux
export type StateFromReducersMapObject<M> = M extends ReducersMapObject
? { [P in keyof M]: M[P] extends Reducer<infer S, any> ? S : never }
: never
infer 待推断类型
// 如果T是继承 any的
type ParamType<T> = T extends (param: infer P) => any ? P : T;
type Unpacked<T> =
T extends (infer U)[] ? U :
T extends (...args: any[]) => infer U ? U :
T extends Promise<infer U> ? U :
T;
// 1. T extends (infer U)[] = false, string 没有继承string[]
// 2. T extends (...args: any[]) => infer U = false, string不是方法类型
// 3. T extends Promise<infer U> = false, string不是Promise类型
// 4. 返回T
type T0 = Unpacked<string>; // string
type T1 = Unpacked<string[]>; // string
// 1. T extends (infer U)[] = false, string 没有继承string[]
// 2. T extends (...args: any[]) => infer U = false, () => string 没有继承该方法类型
// 3. T extends Promise<infer U> = false, string不是Promise类型
// 4. 返回T
type T2 = Unpacked<() => string>; // string
type T3 = Unpacked<Promise<string>>; // string
// 1. T extends (infer U)[] = true, T = Promise<string>, T extends Promise<string>[]
// 2. 返回T
type T4 = Unpacked<Promise<string>[]>; // Promise<string>
type T5 = Unpacked<Unpacked<Promise<string>[]>>; // string
联合类型和extends条件推断
// 条件类型匹配 不是简单的三元运算
type Other = "a" | "b";
type Merge<T> = T extends "x" ? T : Other; // T 等于匹配的类型,然后加上 Other 联合类型一起返回
type Values = Merge<"x" | "y">;
// 得到 type Values = "x" | "a" | "b";
type Other = "a" | "b";
type Merge<T> = T extends "x" ? Other : T; // T 等于除匹配类型的额外所有类型(官方叫候选类型)
type Values = Merge<"x" | "y">;
// 得到 type Values = "a" | "b" | 'y';
// 实际应用
type MakesSense = never extends never ? 'yes' : 'no' // Resolves to 'yes'
// never = 空联合类型 运行 ... extends never将无返回结果 所以 ExtendsNever<never> = never
type ExtendsNever<T> = T extends never ? 'yes' : 'no'
// [T] [never]已经切换比较的类型 所以 ExtendsNever<never> = 'yes'
type ExtendsNever<T> = [T] extends [never] ? 'yes' : 'no'
type MakesSenseToo = ExtendsNever<{}> // Resolves to 'no'
type Huh = ExtendsNever<never> // is yes
新奇语法
// 相当于 user && user.userinfo && user.userinfo.username
const name: string = user?.userinfo?.username
// user一定存在 userinfo一定存在 的语法表示
const name: string = user!.userinfo!.username
infer关键字
export const tuple = <T extends string[]>(...args: T) => args;
// eslint-disable-next-line import/prefer-default-export
export const PresetColorTypes = tuple(
'pink',
'red',
'yellow',
);
// T extends xx T属于xx类型吗 判断语句 如果是 E是待推断类型 如果是E[]
// 下面例子中T 是['pink', 'red', 'yellow'] 所以里面的元素类型为 'pink' | 'red' | 'yellow' 所以E='pink' | 'red' | 'yellow'
export type ElementOf<T> = T extends (infer E)[] ? E : T extends readonly (infer F)[] ? F : never;
// 'pink' | 'red' | 'yellow'
export type PresetColorType = ElementOf<typeof PresetColorTypes>;