When it comes to objects in TypeScript, they are considered open, meaning they allow for additional properties not explicitly defined in the declaration. This characteristic enables interfaces and object types to be easily extended. Without this feature, scenarios like having an interface Bar that extends Foo but is not a valid Foo would arise. However, with TypeScript, {a:string, b:string} can be assigned to {a: string} without any issues.
Sometimes there is confusion surrounding whether TypeScript types are closed, sealed, or exact. The closest concept in TypeScript to these ideas are excess property checks on object literals, which serve more as linter warnings rather than strict type safety checks.
It's essential to note that Pick will always be a supertype of T, allowing Column to be assigned to Pick despite the key K. If you want to exclude certain properties, you must do so explicitly by defining them as optional or using the never type.
To prevent unwanted keys from existing in an object, consider using ExclusivePick instead of Pick:
type ExclusivePick<T, K extends keyof T> =
{ [P in K]: T[P] } & { [P in Exclude<keyof T, K>]?: never }
This code essentially creates an object type that includes only specified keys while prohibiting all other keys in T.
Testing out the ExclusivePick type:
type ColumnSetting = ExclusivePick<Column, 'colId' | 'width' | 'sort'>;
/* type ColumnSetting = {
colId: string;
width?: number | undefined;
sort?: number | undefined;
} & {
something?: undefined;
}*/
If attempting to assign a Column to ColumnSetting, an error will occur due to incompatible property types, emphasizing the importance of specifying allowed keys explicitly.
Playground link to code