Pick, Omit and Record
The generator can also make short cards from the full form. A list of users on the site needs only the id and the name, and a public profile page needs everything except the email. You could describe each card with an interface of its own, but then the fields are written in several places again. Change the type of a field in User, and you have to hunt down every copy.
The utility types Pick and Omit cut the part you need out of an existing type, and Record builds an object type from a list of keys. At the end comes ReturnType, which takes a function's result type.
Pick and Omit
Both take a type and names of its properties, but they do opposite things:
interface User {
id: number;
name: string;
email: string;
age: number;
}
type Card = Pick<User, "id" | "name">;
type PublicUser = Omit<User, "email">;
const card: Card = { id: 1, name: "Ann" };
const shown: PublicUser = { id: 1, name: "Ann", age: 30 };
console.log(card.name, shown.age); // Ann 30Pick
Pick<User, "id" | "name"> keeps only the listed properties, which gives the type { id: number; name: string }. The names are passed as a union of literals, and TypeScript checks each one. You can name only a real property of User, and tsc answers Pick<User, "phone"> with an error:
error TS2344: Type '"phone"' does not satisfy the constraint 'keyof User'.The definition of Pick has the familiar constraint K extends keyof T.
Omit
Omit<User, "email"> does the opposite: it removes the listed properties and keeps all the others, id, name and age. It is handy when there is less to remove than to keep.
Both types follow the original. Change the type of name in User, and it changes in both Card and PublicUser. Interviewers like to ask how Pick differs from Omit.
Record
Record<K, V> builds an object from a list of keys: every key in K becomes a property whose value has the type V. That is a handy way to describe tables such as translations, where every language needs a string:
const greetings: Record<"ru" | "en", string> = {
ru: "Привет",
en: "Hello",
};
const stock: Record<string, number> = { apple: 5 };
console.log(greetings.en, stock["apple"]); // Hello 5Let's walk through the example line by line:
Record<"ru" | "en", string>is an object with the propertiesruanden, both holding strings.- Both properties are required: tsc checks that no language is forgotten.
Record<string, number>has any string as a key and a number as a value. It is the same index signature as{ [key: string]: number }, only written shorter.- The program prints
Hello 5.
If you forget a language, say by writing only { ru: "Привет" }, tsc names the missing key. In the message TypeScript 7 prints the keys sorted, so "en" comes first:
error TS2741: Property 'en' is missing in type '{ ru: string; }' but required in type 'Record<"en" | "ru", string>'.ReturnType: the result type
One more utility takes a type from a function. ReturnType<F> is the type of what a function of type F returns. The utility needs the type of the function, not the function itself, so the name is preceded by typeof:
function parseUser(line: string) {
const parts = line.split(",");
return { name: parts[0], age: Number(parts[1]) };
}
type ParsedUser = ReturnType<typeof parseUser>;
const p: ParsedUser = parseUser("Ann,30");
console.log(p.name, p.age); // Ann 30Let's see where the type ParsedUser came from:
- For the result of
parseUserTypeScript infers the type{ name: string; age: number; }. typeof parseUseris the type of the function itself:(line: string) => { name: string; age: number; }.ReturnTypetakes the result type out of that type. You did not have to describeParsedUserby hand.
If you forget typeof, tsc tells you what is missing:
error TS2749: 'parseUser' refers to a value, but is being used as a type here. Did you mean 'typeof parseUser'?Common mistake
A property name in Omit has a typo:
interface User {
id: number;
name: string;
email: string;
}
type Preview = Omit<User, "emial">;
const p: Preview = { id: 1, name: "Ann" }; // ✗error TS2741: Property 'email' is missing in type '{ id: number; name: string; }' but required in type 'Preview'.tsc is silent on the line with Omit. Unlike Pick, Omit does not check the names: its constraint lets through any string, number or symbol, not only keyof T. User has no property emial, there was nothing to remove, and email stayed in Preview. The error surfaced later, at the object without an email. If tsc asks for a property you removed with Omit, check the name in the Omit itself.
Key points
Pick<User, "id" | "name">keeps only the named properties,Omit<User, "email">keeps all but the named ones.Record<"ru" | "en", string>is an object where every key of the union is required and holds astring.ReturnType<typeof f>is the result type of the functionf;typeofis needed because the utility takes a type, not a value.Pickchecks property names, andOmitdoes not: a typo inOmitpasses silently.