satisfies replaced most of my type annotations, and all of my as
You have a config object. You want two things at once: TypeScript should check it against a type TypeScript should remember the exact values you wrote Before TS 4.9 you could have either one. Not both. Annotating loses the detail type Config = Record < string , string | number > const config : Config = { host : ' localhost ' , port : 3000 , } config . port . toFixed () // Error: Property…
TypeScript offers three different ways to deal with type annotations: using `as`, `satisfies`, or conventional annotations. In older versions of TypeScript (before 4.9), developers could choose between either an annotation or a type check, but not both at once. The annotation would check the object but then widen every value to `string | number`. For example, a `Config` object would be checked for its types, but every value would be widened to either a string or a number, losing the original type information.
With the introduction of `satisfies`, TypeScript can now check the object's types against a specified type while preserving the original exact values. An example provided is a `config` object with properties `host` and `port`. Using `satisfies Config`, TypeScript validates the object against the `Config` type, and even though you can still call methods like `toFixed()` on a string value (`port`), an error is thrown at the call site, making it easier to catch mistakes during development.
However, `satisfies` isn't meant to replace conventional annotations in all cases. It's particularly useful when you want to keep a wider type, such as on a variable that might be reassigned, like `state`. Without the annotation, `state` would be inferred as `'idle'`, and any attempt to reassign it to another value would result in a type error. In contrast, using `satisfies` allows the variable to maintain its original type while still being checked against a specified type, such as `'idle' | 'loading' | 'done'`.
The `as` keyword, on the other hand, is an assertion. It tells the compiler to stop reasoning about the type and trust the developer's declaration. This can be problematic, as it removes the compiler's ability to catch mistakes during development. For instance, using `as Config` on an object with a typo in the property name (`prot` instead of `port`) would compile without any issues, and the typo would only surface when the object is used later in the code.
`Satisfies` is particularly beneficial in scenarios like defining route or event maps, where the keys are literal strings. By using `satisfies Record<string, '/${string}'>`, TypeScript ensures that the keys remain literal, allowing downstream code to remain exhaustive. It also works well with discriminated unions in arrays, where each element is checked against a specified type, and narrowing still works afterward.
In conclusion, TypeScript developers should use `as` when they have more information than the compiler can infer, `satisfies` when they want the object to be checked against a type without widening values, and conventional annotations when they need the wider type. By understanding when to apply each method, developers can more effectively use TypeScript's type system to catch errors and write safer code.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.