This article is a further guide to the types of Omit tools mentioned in geek Time and Chatter, but you don’t need to have watched the podcast to include the prior information.

Pick to Omit

Pick and Omit are both TypeScript built-in tool types that serve a similar purpose, tailoring interfaces such as

interface Foo {
	a: number;
	b: string;
	c: boolean;
}

// { a:number; }
type OnlyA = Pick<Foo, "a">;

// { b: string; c: boolean}
type ExcludeA = Omit<Foo, "a">;
Copy the code

The two functions are opposite, which represents a concept in TypeScript type programming: Almost all tool types have reverse implementations, usually in one of two ways: a simple conditional change to a tool type results in an opposite tool type (Partial and Required), or reverse types are implemented based on forward types (Pick and Omit).

Let’s jump right into the implementation of Pick and Omit:

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Copy the code

Pick simply uses the index type (index signature and index type access) and the mapping type and keyof operator, which will not be expanded here.

Keyof operators often and interface structure are used together, get a set of objects the key value of literal type joint type, such as’ a ‘|’ b ‘|’ c ‘. We also use keyof any to indicate union types whose members are unknown.

Omit is a bit more interesting and is implemented based on the Pick type. Conversely, its second generic argument K does an additional conversion when passing in a Pick type. Exclude

may be a bit roundabout, but in fact:

type Exclude<T, U> = T extends U ? never : T;
Copy the code

Exclude<Set1, Set2> excludes <Set1, Set2>

  • Exclude < ‘a’ | ‘b’ | ‘c’, ‘a’ | ‘d’ | ‘e’ > as the result of the ‘b’ | ‘c’, namely in the characters, and no part of the Set2.

See “Distributed condition Type Omniscience” published at the same time by this account for the realization of difference, complement, union, and intersection of distributed condition types and the extension of their application to two-dimensional object types.

Back to Pick and Omit scenario, Exclude < keyof Foo, ‘a’ > is the result of the ‘b’ | ‘c’, is equivalent to the thinking of and Pick a reverse, so that we get the Omit.

To look at the Omit types, you will find a very strange place: when their functions are cutting object, then the second generic parameter K should be constraint for keyof T, why only Pick bound?

Why do you Omit anything?

You can read more about the discussion in #30825. The following sections are excerpts from the discussion and my own insights.

The Omit tool type was introduced in TypeScript version 3.5, the looser version we see today. In fact, the original PR #30455 Omit Omit operation is strictly enforced:

type Omit<ObjectType, KeysType extends keyof ObjectType> = Pick<ObjectType, Exclude<keyof ObjectType, KeysType>>;
Copy the code

But the implementation eventually introduced by Daniel Rosenwasser (TypeScript PM and now author of every DevBlog issue) removes the strict constraints, see #30552.

Daniel explained that the decision was made mainly because of the existing type of Exclude tool, which also did not restrict the second parameter to be a subset of the first parameter:

type Exclude<T, U> = T extends U ? never : T;
Copy the code

Therefore, the team believes that Omit Omit types should not be restricted in this way.

In my opinion, the two cases are not the same. Exclude, which means we consider the difference set, does not require this restriction in mathematical terms, i.e. :

If we want parameter 2 to be a subset of parameter 1, we should call it Complement:

export type Complement<A, B extends A> = Exclude<A, B>;
Copy the code

The Omit type is actually more similar to the case of the complement. The part that we can remove is the subset of the original object, so the OmitKeys should also be restricted.

In #30825, Sindre Sorhus, the god of NPM (and why, because the NPM package you’re using is most likely directly or indirectly dependent on his open source package), points out that in many TypeScript type libraries, Don’t use built-in Omit types directly, but rather implement a strict version yourself. These libraries include Type-Zoo, Type-Fest (by far the most popular type library, also by Sindre Sorhus), Utility-types, etc.

The TS team believed that changing to a strict version would cause problems with many DefinitelyTyped (@types/ XXX) packages, so it would be better to keep the implementation as it is not always going to please everyone.

Ryan Cavanaugh, another core member of the team, points out that not all situations are better with this constraint, as we need to use keyof Obj2 to eliminate Obj1:

type Omit1<T, K> = Pick<T, Exclude<keyof T, K>>;
type Omit2<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

// Don't Omit anything
declare function combineSpread<T1.T2> (obj: T1, otherObj: T2, rest: Omit1<T1, keyof T2>) :void;

type Point3d = { x: number.y: number.z: number };

declare const p1: Point3d;

// Error can be detected, y is missing in REST
combineSpread(p1, { x: 10 }, { z: 2 });
Copy the code

Seriously, I don’t think that’s really what the Omit type should do, it might be more appropriate to call it Remove…

Much more imaginative discussion follows, such as explicit behavior control through lib:[‘omit ‘] / lib:[‘omit ‘], Ban Types rules for TypeScript ESLint disallow the use of Omit Types, etc.

Of course, since the Omit type we’re seeing today is still the loose version, the community still hasn’t convinced the team. Ryan summarized several reasons in his final Close Comment:

  • Not everyone wants strict Omit types built in, with only 70% of proponents at most.
  • StrictOmit is contagious and can cause problems with a number of downstream dependency type declarations, making it more intuitive for developers to choose between loose or strict.
  • Even if TS Omit the name, community may also use StrictOmit, Except (type-fest), Remove, etc.

extension

The following extensions are unrelated to the main idea of this article and are extensions of the Pick and Omit types. You are welcome to use them as exercises for further independent research.

  • Pick and Omit are clipped by keys. Implement value-based clipping of PickByValue and OmitByValue types.
  • Building on the previous problem, implement strict value-based clipping, such as requiring a full match for a key-value type of a union type (e.gPickByValue<T, 'a'|'b'|'c'>Keys of type ‘a’ cannot be reserved.