generic-tips
Tip 1: Generics on the Type Level
Generics allow the creation of type functions, making code more reusable.
type MyGenericType<T> = {
data: T;
};
const example1: MyGenericType<string> = {
data: "Some string",
};
Tip 2: Passing Type Arguments to Functions
Functions can have type arguments to ensure return types are as expected.
function makeFetch<T>(url: string): Promise<T> {
return fetch(url).then((res) => res.json());
}
const result = makeFetch<{ firstName: string; lastName: string }>("api/user");
Tip 3: Passing Type Arguments to Set
You can specify type arguments in built-in JavaScript collections.
const mySet = new Set<number>();
mySet.add(1);
// mySet.add("string"); // This will cause an error
Tip 4: Inferring the Types
TypeScript can infer type arguments from the provided values.
function addIdToObject<T>(obj: T): T & { id: string } {
return {
...obj,
id: "unique-id",
};
}
const result = addIdToObject({ firstName: "John", lastName: "Doe" });
Tip 5: Constraints on Type Arguments
Constraints can be used to restrict the type of arguments a generic type can accept.
type GetPromiseReturnType<T extends Promise<any>> = T extends Promise<infer U> ? U : never;
async function fetchUserData(): Promise<{ name: string }> {
// ...fetch logic
return { name: "Alice" };
}
type UserData = GetPromiseReturnType<ReturnType<typeof fetchUserData>>;
Tip 6: Constraints in Functions
Type constraints in functions ensure that you pass the correct types.
function getKeyWithHighestValue<T extends Record<string, number>>(obj: T) {
return {
key: "highestValueKey",
value: 123,
};
}
const result = getKeyWithHighestValue({ a: 1, b: 2, c: 3 });
Tip 7: Sometimes 'as' is Fine
Type assertions with as
can be used when you are confident about the type more than TypeScript can infer.
function typedObjectKeys<T>(obj: T): Array<keyof T> {
return Object.keys(obj) as Array<keyof T>;
}
const keys = typedObjectKeys({ a: 1, b: 2 });
Tip 8: Multiple Type Arguments
You can use multiple type arguments for more complex generics.
function getValue<TObj, TKey extends keyof TObj>(obj: TObj, key: TKey): TObj[TKey] {
return obj[key];
}
const value = getValue({ a: 1, b: "text" }, "b");
Tip 9: Defaults in Type Arguments
You can provide default type arguments in generics.
function createSet<T = string>() {
return new Set<T>();
}
const mySet = createSet<number>(); // Set<number>
const defaultSet = createSet(); // Set<string>
Tip 10: Integrating with Third-Party Libraries
Generics can help with integrating TypeScript types from third-party libraries like Zod.
import { z } from 'zod';
async function makeZodSafeFetch<T>(url: string, schema: z.ZodType<T>): Promise<T> {
const res = await fetch(url);
const result = await res.json();
return schema.parse(result);
}
const userSchema = z.object({
firstName: z.string(),
lastName: z.string(),
});
const userData = makeZodSafeFetch('api/user', userSchema);
These tips and examples demonstrate the power and versatility of TypeScript generics in developing type-safe and reusable code.