# 函数签名
# 函数类型表达式
function greeter(fn: (a: string) => void) {
fn("Hello, World");
}
function printToConsole(s: string) {
console.log(s);
}
greeter(printToConsole);
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
type GreetFunction = (a: string) => void;
function greeter(fn: GreetFunction) {
// ...
fn("Hello, World");
}
function printToConsole(s: string) {
console.log(s);
}
greeter(printToConsole);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 调用签名
type Log = {
tag: string;
(name:string): void;
}
function say(name) {
console.log('object :>> ', name);
}
say.tag = '玩吧'
function pluLog(fn: Log) {
return fn.tag + fn('erer')
}
pluLog(say)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 构造函数签名
可以通过在调用签名之前添加new关键字来编写构造签名
export interface VueConstructor<V extends Vue = Vue> {
new <Data = object, Methods = object, Computed = object, PropNames extends string = never>(options?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): CombinedVueInstance<V, Data, Methods, Computed, Record<PropNames, any>>;
// ideally, the return type should just contain Props, not Record<keyof Props, any>. But TS requires to have Base constructors with the same return type.
new <Data = object, Methods = object, Computed = object, Props = object>(options?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): CombinedVueInstance<V, Data, Methods, Computed, Record<keyof Props, any>>;
new (options?: ComponentOptions<V>): CombinedVueInstance<V, object, object, object, Record<keyof object, any>>;
}
1
2
3
4
5
6
7
2
3
4
5
6
7
声明像 Date
一样的构造器,既可以new
也可以直接调用
interface CallOrConstruct {
new (s: string): Date;
(n?: number): number;
}
1
2
3
4
2
3
4
# 泛型函数
不好的实例
function firstElement(arr: any[]) {
return arr[0];
}
1
2
3
4
5
2
3
4
5
好的实例
function firstElement<T>(arr: T[]):T {
return arr[0]
}
// s is of type 'string'
const s = firstElement(["a", "b", "c"]);
// n is of type 'number'
const n = firstElement([1, 2, 3]);
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
在泛型中,ts会自动推断类型
function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {
return arr.map(func);
}
// Parameter 'n' is of type 'string'
// 'parsed' is of type 'number[]'
const parsed = map(["1", "2", "3"], (n) => parseInt(n));
1
2
3
4
5
6
7
2
3
4
5
6
7
# 类型约束
function longest<Type extends { length: number }>(a: Type, b: Type) {
if (a.length >= b.length) {
return a;
} else {
return b;
}
}
// longerArray is of type 'number[]'
const longerArray = longest([1, 2], [1, 2, 3]);
// longerString is of type 'string'
const longerString = longest("alice", "bob");
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
function minimumLength<Type extends { length: number }>(
obj: Type,
minimum: number
): Type {
if (obj.length >= minimum) {
return obj;
} else {
return { length: minimum };
// 出错了,返回的不是泛型
return { length: minimum } as Type;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 指定类型参数
function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
return arr1.concat(arr2);
}
// const arr = combine([1, 2, 3], ["hello"]); // 错误了❌
const arr = combine<string | number>([1, 2, 3], ["hello"])
1
2
3
4
5
6
7
2
3
4
5
6
7
# 编写泛型函数的准则
- Push Type Parameters Down
- 使用较少的类型参数
- 类型参数应该至少出现两次
# 可选参数 ?
function f(x?: number) {
// ...
}
f(); // OK
f(10); // OK
1
2
3
4
5
6
2
3
4
5
6
回调函数的可选参数
function myForEach(arr: any[], callback: (arg: any, index?: number) => void) {
for (let i = 0; i < arr.length; i++) {
callback(arr[i], i);
}
}
1
2
3
4
5
2
3
4
5
# 函数重载
我们写了两个函数重载签名,又写了一个函数,这个函数有自己的实现签名,尽管这个实现签名又两个可选函数,但是还是不能直接调用
function makeDate(timestamp: number): Date; // 重载签名。
function makeDate(m: number, d: number, y: number): Date;; // 重载签名。
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
if (d !== undefined && y !== undefined) {
return new Date(y, mOrTimestamp, d);
} else {
return new Date(mOrTimestamp);
}
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
const d3 = makeDate(5, 5);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
实现签名还必须与重载签名兼容。
function fn(x: string): string;
// Return type isn't right
function fn(x: number): boolean;
function fn(x: string | number):string |boolean {
return "oops";
}
1
2
3
4
5
6
2
3
4
5
6
# 如何写好重载
# 好的案例
实现一个函数,返回字符串或者数组的长度
function len(s: string): number;
function len(arr: any[]): number;
function len(x: any) {
return x.length;
}
1
2
3
4
5
2
3
4
5
不用签名直接写参数
function len(x: any[] | string) {
return x.length;
}
1
2
3
2
3
# 需要知道的其他类型
- void - void 和 undefined不同
- object - object 不是 Object.使用 object!
- unknown - 未知类型表示任何值,和
any
类似,但是更安全.当我们对unknown
进行操作的时候
function f1(a: any) {
a.b(); // OK
}
function f2(a: unknown) {
a.b();
// ❌ Object is of type 'unknown'.
}
function safeParse(s: string): unknown {
return JSON.parse(s);
}
// Need to be careful with 'obj'!
const obj = safeParse(JSON.stringify({name:1}));
console.log('obj.name :>> ', obj.name);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- never 永远不会有返回值的函数,这意味着该函数将引发异常或终止程序的执行。
function fail(msg: string): never {
throw new Error(msg);
}
1
2
3
2
3
- Function - 使用
() => void
代替
# 剩余参数
In TypeScript, the type annotation on these parameters is implicitly any[] instead of any, and any type annotation given must be of the form Array<T>or T[]
, or a tuple type (which we’ll learn about later).
function multiply(n: number, ...m: number[]) {
return m.map((x) => n * x);
}
// 'a' gets value [10, 20, 30, 40]
const a = multiply(10, 1, 2, 3, 4);
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2);
const args = [8, 5] as const;
// OK
const angle = Math.atan2(...args);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 参数解构类型声明
对象的类型注释在解构语法之后:
function sum({ a, b, c }) {
console.log(a + b + c);
}
sum({ a: 10, b: 3, c: 9 });
// 解构加类型声明
function sum({ a, b, c }: { a: number; b: number; c: number }) {
console.log(a + b + c);
}
// 使用类型别名
type ABC = { a: number; b: number; c: number };
function sum({ a, b, c }: ABC) {
console.log(a + b + c);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15