// 参数 first 和 second 分别为 T 和 U 两个泛型
// 返回结果为 T 和 U 的交叉类型
function extend <T, U> (first: T, second: U) : T & U {
const res = <T & U> {}
for (let id in first) {
(<T>res)[id] = first[id]
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<U>result)[id] = second[id];
}
}
return res
}
const x = extend({ a: 'hello' }, { b: 42 });
// 现在 x 拥有了 a 属性与 b 属性
const a = x.a;
const b = x.b;
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; };
泛型约束
我们想限制函数去处理任意带有 .length 属性的所有类型,只要传入的类型有这个属性就允许,为此需要列出对 T 的约束
定义一个接口来描述约束条件, 创建一个包含 .length 属性的接口,使用这个接口和extends关键字来实现约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
declare function get(id: string | number): string
declare function get(name:string,age:number): string
class
declare class Person {
static maxAge: number //静态变量
static getMaxAge(): number //静态方法
constructor(name: string, age: number) //构造函数
getName(id: number): string
}
对象
namespace声明对象
declare namespace OOO{
}
当然了这个对象上面可能有变量,可能有函数可能有类。
declare namespace OOO{
var aaa: number | string
function getName(id: number | string): string
class Person {
static maxAge: number //静态变量
static getMaxAge(): number //静态方法
constructor(name: string, age: number) //构造函数
getName(id: number): string //实例方法
}
}
对象里面套对象也是可以的:
declare namespace OOO{
var aaa: number | string
// ...
namespace O2{
let b:number
}
}
混合类型
既是函数又是对象
declare function $2(s:string): void
declare namespace $2{
let aaa:number
}
既是函数,又是类(可以new出来)
// 1、实例方法
interface People{
name: string
age: number
getName(): string
getAge():number
}
interface People_Static{
// 2、构造函数
new (name: string, age: number): People
new (id:number): People
// 3、作为类,调用类上的方法或者变量
staticA():number
aaa:string
// 4、作为函数使用
(w:number):number
(w:string):number
}
declare var People:People_Static
类的实例方法
let p = new People('zs', 33)
p.age
p.name
p.getName()
p.getAge()
类的构造函数
let p1 = new People('zs', 33)
类的静态属性和方法
People.staticA()
People.aaa
作为函数使用
People(32)
模块化(CommonJS)
有时候需要通过require的方式引入模块化的代码
let a = require('abcde')
a.a
a.b
// c 是一个对象
a.c
对应的写法
declare module "abcde" {
export let a: number
export function b(): number
export namespace c{
let cd: string
}
}
导出一个函数本身
declare module "app" {
function aaa(some:number):number
export=aaa
}
导出一个变量或者常量
declare module "ccc" {
const c:400
export=c
}
ES6的模块化(import export)
使用
import { a1, a2 } from "./A"
console.log(a1)
console.log(a2)
对应的 A.d.ts 文件(推荐)
```js
declare var a1: 1
declare var a2: 2
export { a1,a2 }
也可以写成这样
export declare var a1: 1
export declare var a2: 2
default的默认导出
declare var a1: 1
export default a1
使用
import a1 from "./A";
console.log(a1)
模块化下的全局变量
有时候我们定义全局变量的时候需要引入(别人写的)文件,比如这样的,我想声明个全局变量req:
import { Request } from 'express'
declare var req: Request
由于我们当前的d.ts文件使用了import/export语法,那么ts编译器就不把我们通过declare var xxx:yyy当成了全局变量了,那么我们就需要通过以下的方式声明全局变量:
import { Request,Response} from 'express'
declare global {
var req: Request
}
为什么使用Typescript?
1. 类型
基本类型
数组类型的两种表示方法
接口
合并众多类型声明至一个类型声明
let name: Name; name = { first: 'John', second: 'Doe' };
name = { // Error: 'Second is missing' first: 'John' };
name = { // Error: 'Second is the wrong type' first: 'John', second: 1337 };
特殊类型
// 赋值任意类型 power = '123'; power = 123;
// 它也兼容任何类型 let num: number; power = num; num = power;
联合类型
变量 a 可以为 number 或者是 string 类型
交叉类型
从两个对象中创建一个新对象,新对象拥有着两个对象所有的功能
元组
类似js中的数组,可以指定各种类型[typeofmember1, typeofmember2, ......]
字面量类型
你可以使用一个字符串字面量作为一个类型:
可以在一个联合类型中组合创建一个强大的(实用的)抽象:
泛型
设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:
2. 泛型
使用泛型
创建一个函数,使得返回值的类型与传入参数的类型是相同
使用泛型变量
把泛型变量T当做类型的一部分使用,而不是整个类型,增加了灵活性。
也可以这样写
泛型函数loggingIdentity,接收类型参数T和参数arg,它是个元素类型是T的数组,并返回元素类型是T的数组。
泛型类型
泛型函数的类型与非泛型函数的类型没什么不同,只是有一个类型参数在最前面,像函数声明一样:
使用对象字面量来定义泛型函数
使用泛型接口
我们可能想把泛型参数当作整个接口的一个参数
使用 GenericIdentityFn的时候,还得传入一个类型参数来指定泛型类型(这里是:number),锁定了之后代码里使用的类型。
泛型类
泛型类使用( <>)括起泛型类型,跟在类名后面
泛型约束
我们想限制函数去处理任意带有 .length 属性的所有类型,只要传入的类型有这个属性就允许,为此需要列出对
T
的约束 定义一个接口来描述约束条件, 创建一个包含 .length 属性的接口,使用这个接口和extends
关键字来实现约束3. 如何编写 d.ts 文件
d.ts
是ts
中的声明文件,编写声明文件的好处是可以帮助我们在写代码的时候能够看到编辑器的提示常用的前端库的声明文件(
d.ts
)可以在DefinitelyTyped中找到通过npm i @types/xxx
安装全局类型
变量
关键字
declare
表示声明的意思。在d.ts文件里面,在最外层声明变量、函数或者类要在前面加上这个关键字。在typescript的规则里面,如果一个.ts、.d.ts文件如果没有用到import或者export语法的话,那么最顶层声明的变量就是全局变量。函数
同一个函数有若干种写法:
d.ts对应的写法:
class
对象
namespace
声明对象当然了这个对象上面可能有变量,可能有函数可能有类。
对象里面套对象也是可以的:
混合类型
既是函数又是对象
既是函数,又是类(可以new出来)
模块化(CommonJS)
有时候需要通过
require
的方式引入模块化的代码对应的写法
导出一个函数本身
导出一个变量或者常量
ES6的模块化(import export)
使用
console.log(a1) console.log(a2)
也可以写成这样
default
的默认导出使用
模块化下的全局变量
有时候我们定义全局变量的时候需要引入(别人写的)文件,比如这样的,我想声明个全局变量req:
由于我们当前的d.ts文件使用了
import/export
语法,那么ts编译器就不把我们通过declare var xxx:yyy
当成了全局变量了,那么我们就需要通过以下的方式声明全局变量:UMD
有一种代码,既可以通过全局变量访问到,也可以通过require的方式访问到。比如我们最常见的jquery
按照全局的方式写d.ts,写完后在最后加上declare module "xxx"的描述:
内置对象的扩展
有时候我们扩展了一些内置对象。比如我们给Date增加了一个format的实例方法:
参考:
https://jkchao.github.io/typescript-book-chinese/ https://segmentfault.com/a/1190000009247663 https://www.tslang.cn/docs/handbook/generics.html