对象类型接口
接口函数类型定义
//接口定义
interface fn {
(x: number, y?: string): number
}
//变量定义
let fn1: (x:number, y?: string) => number
//类型别名定义
type fn2 = (x: number, y?: string) => number
function fn(...rest: number[]): number
function fn(...rest: string[]): string
<!--函数实现-->
function fn (...rest: any[]): any {
if (rest[0] && typeof rest[0] === 'string') {
return rest.reduce((prev, next) => prev + next)
} else if (rest[0] && typeof rest[0] === 'number') {
return rest.reduce((prev, next) => prev * next)
}
}
泛型
- 函数泛型定义
-
function log<T>(val: T):T {
return value
}
- 接口泛型定义
-
interface log<T> {
(value: T): T
}
<!--接口函数方法泛型-->
interface log {
<T>(value: T): T
}
- 别名泛型定义
-
type log = <T>(value: T) => T
- 泛型类
-
class Log<T> {
run(value: T): T {
return value
}
}
<!--泛型不能约束类的静态成员-->
<!--静态成员不能引用类类型参数-->
class Log<T> {
static run(value: T): T {
return value
}
}
- 通过接口约束泛型
-
interface Length {
length: number
}
class Log<T extends Length> {
run(value: T): T {
return value
}
}
<!--传入的值必须具有length属性-->
new Log().run([])
- 总结:
- 泛型可以不必谢多条函数重载,冗长的联合声明,增强代码可读性
- 灵活控制类型之间的约束
类型检查机制
- 类型推断
- 基础类型推断
-
let a = 1// a: number
- 联合类型推断
-
let a = [1, null, '1'] //a: (string | number)[]
let b = (x = 1) => x + 1 //b: (x?: number) => number
- 上下文类型推断
-
window.onkeydown = function (event) {
}
- 类型断言
-
interface Foo {
name: string
}
<!--以下断言都可以-->
let foo = {} as Foo
let foo = <Foo>{}
<!--赋值-->
foo.name = 'lk'
- 类型兼容
- 接口兼容(鸭式辨型)
-
interface T1 {
x: any
y: any
}
interface T2 {
x: any
y: any
z: any
}
let T11: T1 = {
x: 1,
y: 2
}
let T22: T2 = {
x: 1,
y: 2,
z: 3
}
<!--类型少的兼容类型多的-->
T11 = T22 //正确
T22 = T11 //错误
- 函数兼容(类型少的兼容类型多的)
-
type Handler = (a: number, b: number) => void
function LK(handler: Handler): Handler {
return handler
}
let handler1 = (a: number) => a
let handler2 = (a: number, b: number) => a + b
let handler3 = (a: number, b: number, c: number) => a + b + c
LK(handler1)
LK(handler2)
//错误
LK(handler3)
- 函数兼容-rest参数
-
let handler1 = (a: number) => {}
let handler2 = (a?: number, b?: number) => {}
let handler3 = (...args: number[]) => {}
//可选参数要想兼容,必须将tsconfig.json中的strictFunctionTypes设置为false
handler1 = handler3
handler2 = handler3
- 枚举兼容(枚举和number类型的兼容)
-
enum Course {
Tools,
Manager
}
let course: Course.Tools = 1
let num: number = Course.Tools
- 类兼容
-
class Parent {
id: number = 1
name: string = ''
}
class Parent1 {
id: number = 1
}
let p1 = new Parent1()
let p = new Parent()
p1 = p
// 错误
p = p1
- 类兼容-私有属性
-
class Parent {
id: number = 1
name: string = ''
private age: number = 12
}
class Parent1 {
id: number = 1
name: string = ''
}
//只有子类才可以和父类实例相互赋值
class Child extends Parent {}
let p = new Parent()
let c = new Child()
c = p
- 泛型兼容
-
<!--没有具体指定类型的泛型,可以相互兼容-->
let fn1 = <T>(x: T): T => {
return x
}
let fn2 = <U>(x: U): U => {
return x
}
fn1 = fn2
- 类型保护
- 联合类型和交叉类型
- 联合类型
-
let LH: number | string = 1
let LH1: 'a' | 'b' | 'c' = 'c'
- 交叉类型
-
class P {
name: string = 'P'
}
class P1 {
age: number = 123
}
let P2: P & P1 = {
name: '',
age: 1
}
- 索引类型
- 索引类型的查询操作符keyof T、T[K]、T extends U(类型约束)
-
interface O {
a: number
b: string
}
let key: keyof O // let key: 'a' | 'b'
- 类型索引和类型约束
-
let O = {
name: 'LK',
age: 18,
sex: '男'
}
function getValue<T, K extends keyof T>(o:T, k:K[]):T[K][] {
return k.map(i => o[i])
}
- 映射类型
- TS内置了很多映射类型,我们先来感受一下
-
interface T1 {
a: string
b: string
}
// 映射完成以后如下
// type T2 = {
// readonly a: string;
// readonly b: string;
// }
type T2 = Readonly<T1>
- 看一下内部是如何实现的
-
//用到了我们之前讲述的索引类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
- 非同态的映射类型,会返回一个新的类型
-
interface T1 {
a: string
b: string
}
type T3 = Record<'string' | 'number', T1>
- 看一下Record的实现
-
type Record<K extends keyof any, T> = {
[P in K]: T
}
- 条件类型
- T extends U ? X : Y(简单的三元类型推断)
-
type T10<T> = T extends string ? string : T extends Function ? Function : any
type T11 = T10<string>
let t10: T11 = '21'
- (T | K) extends U ? X : Y
-
type Exclude<T, U> = T extends U ? never : T;
- 延迟推断 infer
-
//这个稍微复杂点,我们可以利用上面讲到的条件类型来分析
// 泛型 T 必须要是一个函数类型的参数
// 泛型 T 如果可以集成于一个函数,则延迟推断返回值,预先定义一个R
// 执行过程中,根据传入的函数类型,返回正确的R,否则any
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
//看一个实际的例子
const fn = (count: number) => count * 100
type T1 = typeof fn
// T2为number
type T2 = ReturnType<T1>
- namespace命名空间声明
- 声明合并,声明合并指的是当名称一样的情况下,ts会进行合并
- 编写声明文件
- ts中声明文件一般放置于@types目录下,在package.json的配置为types下对应的位置,下例为antd
-
"typings": "lib/index.d.ts"
- 声明文件以 .d.ts结尾
-
//全局声明文件,这里声明namespace可以做到声明合并,且不污染全局声明
//global.d.ts
declare function Global () {}
declare namespace Global {
const version: string
}
- 模块声明文件
-
//module.d.ts
declare function Module {}
namespace Module {}
export = Nodule
- UMD模块声明文件
-
//module.d.ts
namespace UmdModule {}
export as namespace UmdModule
export = UmdModule
- 文件内部扩展声明
-
declare module 'myModule' {
}
declare global {
}
- ts和babel结合