Created
June 4, 2019 19:19
-
-
Save jarhoads/6122577c0b546d5a86cc5b4407847c31 to your computer and use it in GitHub Desktop.
typescript generics
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// it is possible to create generic functions, including generic methods, generic interfaces, and generic classes. | |
// Functions | |
// To make a function generic, you add a type parameter enclosed in angle brackets (< >) | |
// immediately after the function name. | |
// When you call a generic function, you can specify the type argument by | |
// placing it in angle brackets after the function name. | |
function reverse<T>(list: T[]) : T[] { | |
const reversedList: T[] = []; | |
for (let i = (list.length - 1); i >= 0; i--) { | |
reversedList.push(list[i]); | |
} | |
return reversedList; | |
} | |
// the type arguments can be omitted because the compiler is able to infer the type based on the arguments passed to the function. | |
const letters = ['a', 'b', 'c', 'd']; | |
// d, c, b, a | |
const reversedLetters = reverse<string>(letters); | |
const numbers = [1, 2, 3, 4]; | |
// 4, 3, 2, 1 | |
const reversedNumbers = reverse<number>(numbers); | |
// Interfaces | |
// the type parameters are placed directly after the interface name. | |
class CustomerId { | |
constructor(private customerIdValue: number) { } | |
get value(){ | |
return this.customerIdValue; | |
} | |
} | |
class Customer { | |
constructor(public id: CustomerId, public name: string) { } | |
} | |
interface Repository<T, TId>{ | |
getById(id: TId): T; | |
persist(model: T): TId; | |
} | |
class CustomerRepository implements Repository<Customer, CustomerId> { | |
constructor(private customers: Customer[]) { } | |
getById(id: CustomerId) { | |
return this.customers[id.value]; | |
} | |
persist(customer: Customer) { | |
this.customers[customer.id.value] = customer; | |
return customer.id; | |
} | |
} | |
// Classes | |
// generic classes can save even more by supplying a single implementation to service many different type scenarios. | |
// The type parameters follow the class name and are surrounded by angle brackets. | |
// The type parameter can be used to annotate method parameters, properties, return types, and local variables within the class. | |
// a generic class to provide a single implementation for all named ID types in a domain model. | |
// This allows all ids to be named without requiring individual implementations for each named type. | |
// This is a common pattern that prevents accidental substitution of values. | |
class DomainId<T> { | |
constructor(private id: T) { } | |
get value(): T { | |
return this.id; | |
} | |
} | |
class OrderId extends DomainId<number> { | |
constructor(orderIdValue: number) { | |
super(orderIdValue); | |
} | |
} | |
class AccountId extends DomainId<string> { | |
constructor(accountIdValue: string) { | |
super(accountIdValue); | |
} | |
} | |
// Examples of compatibility | |
function onlyAcceptsOrderId(orderId: OrderId) { | |
// ... | |
} | |
function acceptsAnyDomainId(id: DomainId<any>) { | |
// ... | |
} | |
const accountId = new AccountId('GUID-1'); | |
const orderId = new OrderId(5); | |
// Error: Argument of type 'AccountId' is not assignable to parameter of type 'OrderId' | |
// onlyAcceptsOrderId(accountId); | |
// OK | |
onlyAcceptsOrderId(orderId); | |
// OK | |
acceptsAnyDomainId(accountId); | |
// Type Constraints | |
// A type constraint can be used to limit the types that a generic function, interface, or class can operate on | |
// to specify a contract that all types must satisfy to be used as a type argument. | |
// Type constraints are specified using the extends keyword, | |
// whether the constraint is an interface, a class, or a type annotation that describes the constraint. | |
// If a type argument is specified that does not satisfy the constraint, the compiler will issue an error. | |
// The constraint also allows the TypeScript language service to supply autocompletion suggestions for the generically typed members. | |
// You can only specify a single class in a type constraint. | |
// Although you cannot specify multiple classes in a type constraint, | |
// you can create an interface that extends multiple classes and uses the interface as the constraint, | |
// which achieves the same result. | |
// Any types used with the constraint would then need to satisfy | |
// all the class signatures that have been combined into the single interface. | |
interface HasName { | |
name: string; | |
} | |
class Personalization { | |
static greet<T extends HasName>(obj: T) { | |
return 'Hello ' + obj.name; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment