TypeScript: Assertions
Assertions
In TypeScript, assertions are expressions used to inform the TypeScript compiler about the type of a variable or expression when the type is known to you but cannot be automatically inferred by the compiler. Assertions are a way to provide explicit type information, essentially overriding TypeScript's inherent type inference mechanisms.
Here are the built-in assertions in TypeScript.
as const
The as const assertion converts a variable to a constant, making all its properties readonly.
let example = { x: 10, y: 20 } as const;
// example
x = 15; // Error: Cannot assign to 'x' because it is a read-only property.
as [type]
The as [type] assertion specifies the type of an expression where the type is known but TypeScript cannot infer it.
let someValue: any = "This is a string";
let strLength: number = (someValue as string).length;
another example
interface Person {
name: string;
age: number;
}
// Let's say we have a variable `unknownPerson` of type `any`:
let unknownPerson: any = { name: "Alice", age: 30 };
// We know more about the type, so we assert it to the `Person` type:
let person: Person = unknownPerson as Person;
console.log(person.name); // Output: Alice
console.log(person.age); // Output: 30
as any
The as any assertion opts-out of type checking for a variable.
let vaguelyTyped: any = "could be anything";
let strLength: number = (vaguelyTyped as any).length;
Non-null Assertions
Non-null assertions are a way to tell the TypeScript compiler that you are certain a value will not be null or undefined at the time it is used, even when TypeScript's type checking might consider such a case possible. This assertion can be particularly useful when you are confident about the presence of a value due to the logic of your application, but TypeScript's static analysis cannot infer this certainty.
Syntax of Non-null Assertions
- Postfix
!Operator: Placed after the variable or expression, it removes null and undefined from the type. - Using
asType Assertion: Explicitly asserting that the variable is not null or undefined.
Example Usage
// Assume we have an optional interface property which may be undefined
interface User {
id: number;
name?: string;
}
let user: User = { id: 123 };
// Using the non-null assertion to access 'name' which is optionally undefined
let userName: string = user.name!; // We assert that name is definitely not null or undefined here
// Using 'as' type assertion
let userName2: string = user.name as string; // Similarly asserting non-nullability
console.log(userName); // Note: This will throw a runtime error if name is actually undefined
Satisfies Keyword
The satisfies keyword in TypeScript is a relatively new feature introduced in TypeScript 4.9.
It is used for type assertion, allowing you to check if a value satisfies a particular type without changing the resulting type of the expression.
This keyword is particularly useful in situations where you want to ensure that a value conforms to a specific interface or type, but you don't want to change its original type.
Purpose of the satisfies Keyword
The satisfies keyword helps with:
- Type-checking against an interface or type: Ensuring that a value matches a specific structure without altering its type.
- Ensuring API contract compliance: Useful when implementing a function or component expected to meet certain type requirements.
- Documentation and readability: Clearly communicates the intended type conformity without affecting the underlying type of the data.
How It Works
The satisfies keyword acts like a type assertion but does not perform type casting. Instead, it checks that the value can be assigned to the type specified after satisfies and if not, TypeScript will raise a type error at compile time. However, the expression itself still retains its original type.
Example Usage
interface User {
id: number;
name: string;
email: string;
}
const user = {
id: 1,
name: "Alice",
email: "alice@example.com",
isAdmin: true,
};
// Using 'satisfies' to ensure 'user' matches the 'User' interface
user satisfies User;
// 'user' still retains its original type with 'isAdmin' property
console.log(user.isAdmin); // Outputs: true
// If we mistakenly add or omit a property, TypeScript will throw an error
const newUser = {
id: 2,
name: "Bob",
// email is missing here
isAdmin: false,
} satisfies User; // TypeScript Error: Property 'email' is missing in type ...
When to Use satisfies
- Validating Shape without Narrowing: Use it when you need to validate that an object meets a specific type interface without wanting to narrow the type of the object to that interface.
- Advanced Configuration Objects: Particularly useful for configuration objects where you expect certain properties to meet specific criteria.
- Complex Conditions: Where multiple types or interfaces may apply to a single object, and you need to ensure it meets one of them without affecting its usability elsewhere in your code.