Definition of generic functions
Basic Concepts of Generic Functions
Generic functions in TypeScript allow us to create reusable components that can work with multiple types rather than a single type. Generics are declared by adding <T>
after the function name, where T
is a type variable representing any type.
function identity<T>(arg: T): T {
return arg;
}
This simple identity
function takes an argument of type T
and returns a value of the same type. The type can be explicitly specified when calling:
let output = identity<string>("hello");
Alternatively, TypeScript can automatically infer the type:
let output = identity("hello"); // Type inferred as string
Generic Type Parameters
Generic functions can accept multiple type parameters:
function merge<U, V>(obj1: U, obj2: V): U & V {
return {...obj1, ...obj2};
}
const result = merge({name: "Alice"}, {age: 30});
// result type is {name: string} & {age: number}
Type parameters can also have constraints:
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
let x = {a: 1, b: 2, c: 3};
getProperty(x, "a"); // Correct
getProperty(x, "d"); // Error: "d" is not a property of x
Type Inference in Generic Functions
TypeScript can automatically infer generic types based on the arguments passed:
function map<T, U>(array: T[], callback: (item: T) => U): U[] {
return array.map(callback);
}
const numbers = [1, 2, 3];
const strings = map(numbers, (n) => n.toString());
// strings type is string[]
When type inference is unclear, type parameters can be explicitly specified:
const strings = map<number, string>(numbers, (n) => n.toString());
Generic Functions and Interfaces
Generic functions can be combined with interfaces:
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
Generic interfaces can also be created to describe function types:
interface CreateArrayFunc {
<T>(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc = function<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
Generic Constraints
Constraints can be added to generic parameters using the extends
keyword:
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity("hello"); // Correct, strings have a length property
loggingIdentity(3); // Error, numbers do not have a length property
Type parameters can also constrain other type parameters:
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
Default Generic Types
TypeScript 2.3 introduced default types for generic parameters:
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
const strArray = createArray(3, "x"); // string[]
const numArray = createArray<number>(3, 1); // number[]
Generic Function Overloading
Generic functions can be combined with function overloading to provide more precise type checking:
function process<T extends string | number>(input: T): T extends string ? string : number;
function process(input: any) {
if (typeof input === "string") {
return input.toUpperCase();
} else {
return input.toFixed(2);
}
}
const strResult = process("hello"); // string
const numResult = process(3.1415); // number
Advanced Generic Patterns
Conditional types combined with generic functions can implement complex type logic:
type Flatten<T> = T extends Array<infer U> ? U : T;
function flatten<T>(array: T): Flatten<T> {
return Array.isArray(array) ? array[0] : array;
}
const nested = [[1, 2], [3, 4]];
const flat = flatten(nested); // number[]
Mapped types combined with generic functions:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
function freeze<T>(obj: T): Readonly<T> {
return Object.freeze(obj);
}
const frozen = freeze({x: 10, y: 20});
frozen.x = 30; // Error: Cannot assign to 'x' because it is a read-only property
Generic Functions and Classes
Generic functions can also be used as class methods:
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; };
Static methods can also use generics:
class ArrayUtils {
static first<T>(array: T[]): T | undefined {
return array[0];
}
}
const firstItem = ArrayUtils.first([1, 2, 3]); // number | undefined
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn