- #110
Thoughts on Validating Exact Type Equality in TypeScript
type User = {name: string;};function printUser(user: User) {console.log(user);}printUser({ name: 'Joe', email: 'joe@email.com' });// ❌ Error: Object literal may only specify known properties, and 'email' does not exist in type 'AllowedType'.const userData = {name: 'Joe',email: 'joe@email.com',};printUser(userData);// ✅ No errorTypeScript follows a Structural Type system, meaning that if two types have the same structure, in this case, “objects with the same property names and types” they are considered the same, even if they have different type names. Therefore, when passing a variable to a function, TypeScript determines type compatibility based on structure; as long as the variable contains all the properties required by the function, they are compatible.
However, when directly passing an object literal to a function, TypeScript performs stricter checks, requiring the object literal to fully conform to the type definition of the function parameters due to the concept of Freshness. In other words, this is Strict Object Literal Checking.
This checking mechanism is primarily to enhance the accuracy and reliability of the code, avoiding spelling mistakes.
If you want TypeScript to perform strict type checking, here is a generic workaround for
Exact
that can be used:type Exact<A, B> = A extends B ? (B extends A ? A : never) : never;type User = {name: string;};function printUser<T>(user: Exact<T, User>) {console.log(user);}printUser({ name: 'Joe', email: 'joe@email.com' });// ❌ Error: Argument of type '{ name: string; email: string; }' is not assignable to parameter of type 'never'.const userData = {name: 'Joe',email: 'joe@email.com',};printUser(userData);// ❌ Error: Argument of type '{ name: string; email: string; }' is not assignable to parameter of type 'never'Further Reading:
- #109
- #108
- #107
- #106
- #105
- #104
- #103
- #102
- #101
- #100
- #99
- #98
- #97
- #96
- #95
- #94
- #93
- #92
- #91
- #90
- #89
- #88
- #87
- #86
- #85
- #84
- #83
- #82
- #81
- #80
- #79
- #78
- #77
- #76
- #75
- #74
- #73
- #72
- #71
- #70
- #69
- #68
- #67
- #66
- #65
- #64
- #63
- #62
- #61
- #60
- #59
- #58
- #57
- #56
- #55
- #54
- #53
- #52
- #51
- #50
- #49
- #48
- #47
- #46
- #45
- #44
- #43
- #42
- #41
- #40
- #39
- #38
- #37
- #36
- #35
- #34
- #33
- #32
- #31
- #30
- #29
- #28
- #27
- #26
- #25
- #24
- #23
- #22
- #21
- #20
- #19
- #18
- #17
- #16
- #15
- #14
- #13
- #12
- #11
- #10
- #9
- #8
- #7
- #6
- #5
- #4
- #3
- #2
- #1