I've recently developed a type in Typescript that explicitly blocks specific properties from objects/interfaces. This is necessary because Typescript's excess property checking only kicks in when an object literal is directly assigned, not when it's first assigned to a variable.
For more information on "Excess Property Checks" check out this link: https://www.typescriptlang.org/docs/handbook/interfaces.html
I've encountered situations where I need functions to accept objects without certain predefined properties. While passing in an object literal can solve this issue, there's room for error if the next developer misses this detail. Hence, I decided to completely block these properties (I understand the importance of runtime checks for excess props, but my focus here is on TS).
Mainly, I'm wondering if it's possible to create new types like DisallowBandC
based on the original Disallow
type?
Furthermore, I'm curious if it's feasible to implement Disallow
without forming unions between two types. Any suggestions for simplification are also welcome.
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
type Never<T, K extends keyof T> = { readonly [P in K]?: never };
// Is it achievable without using unions?
type Disallow<T, K extends keyof T> = Omit<T, K> & Never<T, K>;
interface Stuff {
readonly a: string;
readonly b?: number;
readonly c: string | null;
readonly d: null;
readonly e?: null;
readonly f?: undefined;
readonly g: string;
}
type Blocked = 'b' | 'c'
// This setup works fine
export type Disallowed = Disallow<Stuff, Blocked>;
// However, this one does not:
export type DisallowBandC<T> = Disallow<T, Blocked>;
// Receiving TS Error:
// "Type 'Blocked' does not satisfy the constraint 'keyof T'.
// Type '"b"' is not assignable to type 'keyof T'."