A type assertion (value as Type) tells the compiler to treat a value as a specific type without runtime checking. The non-null assertion (value!) tells it a value is not null/undefined. Both override the checker and should be used sparingly.
as is useful when YOU know more than the compiler — e.g. document.getElementById('x') as HTMLInputElement, or response as ApiUser after parsing JSON. It performs NO runtime validation, so a wrong assertion causes a runtime error later. The ! operator removes null/undefined from a type (el!.value) when you're certain it exists. Overusing as/! is a code smell that hides real bugs; prefer proper narrowing (type guards) or validation. Avoid the double-assertion escape hatch (as unknown as X) except at genuine boundaries.
Reading an input in a test: const input = document.querySelector('#email') as HTMLInputElement; input.value — the assertion tells TS it's an input so .value is valid.
A test does const user = data as User and later crashes with 'Cannot read property email of undefined'. Why didn't TypeScript catch it?