TS: Genérico
1. Introdução
-
Às vezes, se quisermos fazer com que os parâmetros e tipos de retorno de uma função sejam iguais, podemos usar variáveis de tipo .
-
Uma variável de tipo é um tipo especial de variável usado para representar um tipo em vez de um valor.
function identity<T>(arg : T) : T{ return arg; }
-
Depois que a função genérica é definida, ela pode ser usada de duas maneiras.
-
Uma é passar todos os parâmetros, incluindo os parâmetros de tipo:
let out = identity<string>("yivi");
-
A outra é usar a inferência de tipo, ou seja, o compilador inferirá automaticamente o tipo:
let out = identity("yivi");
-
A inferência de tipo nos ajuda a manter o código conciso e altamente legível.
2. Variáveis genéricas
-
Vamos dar uma olhada em um exemplo:
function foo<T>(arg : T): T{ console.log(arg.length); // error,arg的类型为T,无明确指示方法,因此报错。 return arg; }
-
Quando queremos manipular um array do tipo T,
.length
a propriedade existe, então nenhum erro será relatado:function foo<T>(args : T[]):T[]{ console.log(args.length); return args; } // or function foo<T>(args : Array<T>) : Array<T>{ console.log(args.length); return args; }
3. Tipos genéricos
-
Podemos usar diferentes nomes de parâmetros genéricos, desde que o número e o uso sejam consistentes;
-
Você também pode usar literais de objeto com assinaturas de chamada para definir funções genéricas;
function identity<T>(arg : T) : T{ return arg; } let myIdentity : <U>(arg : U) => U = identity; // or let myIdentity : { <T>(arg : T):T} = identity;
-
Pegue o objeto literal acima e escreva-o como uma interface:
interface IdentityInterface{ <T>(arg : T) : T; } function identity<T> (arg : T) : T{ return arg; } let myidentity : IdentityInterface = identity;
-
Também é possível tratar um parâmetro genérico como um parâmetro de toda a interface, de modo a garantir que sabemos qual tipo genérico está sendo usado:
interface IdentityInterface<T>{ (arg : T) : T; } function identity<T> (arg : T) : T{ return arg; } let myidentity : IdentityInterface<string> = identity;
-
As classes genéricas são semelhantes às interfaces genéricas e ambas usam <> para restrições:
class Foo<T>{ state : T; add : (x : T, y : T) => T; } let myfoo = new Foo<number>(); myfoo.state = 1; myfoo.add = (x,y)=>{ return x + y; }
4. Restrições genéricas
-
No exemplo acima, mencionamos
.length
que se este atributo for usado em genéricos, um erro será relatado; -
Portanto, precisamos apenas implementar uma interface e permitir que o tipo genérico tenha esse atributo para evitar que o compilador relate erros;
interface Length{ length : number; } function identity<T extends Length>(arg : T) : T{ console.log(arg.length); return arg; }
-
Embora o compilador não relate um erro, se você usar um tipo sem esse atributo, ele também relatará um erro;
-
Portanto, precisamos passar um valor que atenda às restrições e contenha os atributos necessários;
identity({ length : 10,value : 100});
5. Use tipos de classe em genéricos
-
Ao usar genéricos para criar funções de fábrica no TypeScript, você precisa se referir ao tipo de classe do construtor.
function create<T> (c : { new() : T}) : T{ return new c(); } class Foo{ name : string; } let a : Foo = create(Foo); a.name = 'yivi' console.log(a.name)