Repository navigation
Safe type assertion (upcast / widening) operatorΒ #56235
Description
π Search Terms
as, satisfies, type assertion, type casting
β Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
β Suggestion
I suggest adding a safe type assertion operator, a kind of middle ground between current as and satisfies.
- Like
as, it should cast the expression to the asserted type - Like
satisfies, it should only allow assertion if the asserted type is wider than the expression type
Here's what it might look like:
type Foo = { a: number }
const foo1 = { a: 1 } as! Foo // OK. foo1: Foo
const foo2 = { a: 1, b: 2 } as! Foo // OK, foo2: Foo
const foo3 = { b: 2 } as! Foo // Error, { b: number } is not assignable to Foo
const foo4 = { } as! Foo // Error, {} is not assignable to Fooπ Motivating Example
For example, such operator will allow to safely give named types to expressions without declaring extra variables or helper functions.
Consider an async function f that returns an object of type Foo:
type Foo = { a: number }
const f = async () => ({ a: 1 })I want the returned object to strictly be of Foo type, how do I enforce this?
- Hard code the return type
const f = async (): Promise<Foo> => ({ a: 1 })The problem here is that I must write Promise<Foo> instead of just Foo, but the Promise part can be inferred just fine. In real world these generics can be arbitrarily complex.
- Declare a variable
const f = async () => {
const result: Foo = { a: 1 }
return result
}This is better than retyping generics but still pretty verbose.
- Use a helper function
const safeAssert = <T>(value: T): T => value
const f = async () => safeAssert<Foo>({ a: 1 })This is ok but still requires writing/importing helper functions.
as is not an option here because it's unsafe, satisfies is neither because it doesn't name the type. Naming a type can be important for documentation purposes, this proposal allows to do this with less boilerplate.
const f = async () => ({ a: 1}) as! Foo // f: () => Promise<Foo>π» Use Cases
As described above, this feature can be used to safely cast expressions to specified types. A practical example when it can be useful is naming a type in an expression. Currently this is only covered by custom helper functions such as <T>(value: T): T => value.
Activity
RyanCavanaugh commentedon Oct 27, 2023
This was discussed a bit at #47920. Your example is missing what I'd consider the most straightforward solution:
bgenia commentedon Oct 27, 2023
This has the same problem as my 1st example, you have to retype generics
Here's a more complex example to show why it's inconvenient:
Now I have to type 3 generics:
RyanCavanaugh commentedon Oct 27, 2023
This only applies to
Promise, though. In all other cases, you're writing the full type again.bgenia commentedon Oct 27, 2023
I used promises because it's the most obvious example, but you can have all kinds of functions that take your value and wrap it in different generics. I updated my example to demonstrate this.
Josh-Cena commentedon Oct 28, 2023
The most painful thing this would remedy is cases such as #9998, which forces you to do
true as boolean. I believe there are "best practices" these days that force you to dox satisfies T as Tβbased on feature requests we've closed in ts-eslint.raythurnvoid commentedon Jan 4, 2024
We would find this feature very useful since by using playwright, we create mocks for our application and we are using
satisfiesto ensure that the mock is correct for the given type. however we also have to useasto provide better intellisense in vscode for the given mock resulting in manysatisfies T as Twhere T may be a very long string since we use types generated by grpc.saltman424 commentedon Mar 28, 2024
I was about to create a feature request, but I think this is asking for the same thing. Basically, I just want something like
X satisfies as Tto be syntactic sugar forX satisfies T as T. As mentioned by Joshua Chen (@Josh-Cena) and @raythurnevoid, this seems to be a common and sometimes verbose pattern. Just some simple syntactic sugar to remove the duplication ofTwould be fantastic.Also, just to link it, this feature would pretty much address: #13626
jcalz commentedon Jul 31, 2025
cross-linking
satisfiesas,satisfias,sassafras#61789 (comment)
#51853 (comment)
[-]Safe type assertion operator[/-][+]Safe type assertion (upcast / widening) operator[/+]5cover commentedon Mar 27, 2026
This is effectively type ascription.
One question : would the operator model type annotation semantics 1:1 (notably assignability + excess property checks on fresh object literals)?
Example:
Since I keep finding myself needing this too, here are my two cents regarding the syntax:
I think
x ascribe Twould fit in best for modern TypeScript though the terminology might be confusing to beginners.mkantor commentedon May 25, 2026
In vacuum I like
ascribeorwidenfor this keyword, but my vote actually goes tox satisfies as T1βit requires no new keywords, is an easy thing to write if you have an existingsatisfiesorasclause, and is currently a syntax error. It's even naturally discoverable as folks will end up typing it on the way fromx as Ttox satisfies T as T.Footnotes
I think
x as satisfies Tshould continue to be a syntax error. I could imagine it desugaring tox as T satisfies Tfor symmetry, but that's a pointless thing to write and I can't imagine anyone intentionally wanting this, only writingx as satisfies Tby accident when they intended to writex satisfies as Tβ©michaelboyles commentedon Jun 20, 2026
Matt Kantor (@mkantor)
isis better IMO.isis currently only used in type guards, sox is Talso currently a syntax error in this context. For a type guard, the return type is specifying whether the type is satisifed. For this feature, the programmer is specifying that the type is satisfied (and should be an error if it isn't). I think there's a nice symmetry there.It's also shorter and reads like English: here's an
xwhich is aT.The biggest isssue is that
x is Tcan be visually mistaken forx as T. The problem mostly comes when you writeasand meantis, rather than the opposite, sinceasis unsafe. But tooling can probably mitigate this. A good linter rule could probably determine thatiswas intended.Since it's only a single word, it doesn't have the cognitive burden of remembering
satisfies asoras satisfiesthat you mentioned.