阿里云主机折上折
  • 微信号
Current Site:Index > Definition of generic functions

Definition of generic functions

Author:Chuan Chen 阅读数:9845人阅读 分类: TypeScript

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

上一篇:泛型基础概念

下一篇:泛型接口

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.