Description
Suggestion
Right now, satisfies
works on values and object literals right after they're declared. However, there are use cases when satisfies
can't be used immediately, and the value is imported, such as JSON files.
🔍 Search Terms
JSON, typed JSON, satisfies JSON
✅ Viability Checklist
My suggestion meets these guidelines:
- 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 TypeScript's Design Goals.
⭐ Suggestion
In short, I'd like to be able to do this:
import book from 'book.json'
const foo = book satisfies BookResource
or, alternatively:
import book from 'book.json' with { satisfies: BookResource }
and have it work equal to:
const book = {
// book fields
} satisifies BookResource
📃 Motivating Example
I have this structure in my book data JSON file:
{
"data": [
{
"type": "book",
"baseCode": "394b76ce-3ad9-4c83-b21d-d5eab7962249",
"id": "394b76ce-3ad9-4c83-b21d-d5eab7962249",
"url": "http://github.com/donec/dapibus/duis/at.jpg",
"originalUrl": "http://qq.com/ante/ipsum/primis.json",
"imageUrl": "http://dummyimage.com/246x100.png/5fa2dd/ffffff",
"retailer": "Amazon",
"title": "Dead End Drive-In",
"author": "Vlad Cutmore"
},
{
// more of the same schema
}
]
}
And I have a type (interface) like:
interface BookResource {
id?: string
type: 'book'
baseCode: string
imageUrl: string
title: string
author: string
originalUrl: string
retailer: string
url: string
}
(Note, in this scenario, the source data MUST be JSON.)
I want to make sure that if anyone modifies the JSON, they get a type error. However, doing the following results in an error:
import books from 'books.json'
// ...
const bookData = books.data satisfies BookResource[] // error - "Type {...} is not assignable to BookResource"
Now, I wasn't sure if this should be marked as a bug or feature request, because this works:
const blah = {
type: 'book',
baseCode: '394b76ce-3ad9-4c83-b21d-d5eab7962249',
id: '394b76ce-3ad9-4c83-b21d-d5eab7962249',
url: 'http://github.com/donec/dapibus/duis/at.jpg',
originalUrl: 'http://qq.com/ante/ipsum/primis.json',
imageUrl: 'http://dummyimage.com/246x100.png/5fa2dd/ffffff',
retailer: 'Amazon',
title: 'Dead End Drive-In',
author: 'Vlad Cutmore'
} satisfies BookResource
In other words, the exact same value data, when applied with the satisfies
keyword, causes an error depending on where the value is being checked, even though it's the same value.
What seems to be the case is that satisfies
gently coerces the type (specifically to "book"
instead of string
) in the latter example, but fails to do so when the value is imported.
💻 Use Cases
Currently, I want to use this for JSON, but note that there are other cases where you might want to "check" and gently nudge the type at compile time the exact same way that happens with satisfies
at assignment time.
Because, similar to the above example, this also causes an error:
const foo = {
type: 'book',
baseCode: '394b76ce-3ad9-4c83-b21d-d5eab7962249',
id: '394b76ce-3ad9-4c83-b21d-d5eab7962249',
url: 'http://github.com/donec/dapibus/duis/at.jpg',
originalUrl: 'http://qq.com/ante/ipsum/primis.json',
imageUrl: 'http://dummyimage.com/246x100.png/5fa2dd/ffffff',
retailer: 'Amazon',
title: 'Dead End Drive-In',
author: 'Vlad Cutmore'
}
const bar = foo satisfies BookResource
In TypeScript / JavaScript terms, these should be equivalent statements, and TypeScript should be able to
- say that the value of
foo
satisfies the type ofBookResource
- coerce
bar
to be oftype: "book"
vstype: string
, just as it would ifsatisfies
immediately followed the value.
1st note: obviously I could just use as BookResource
instead of satisfies BookResource
, but of course that bypasses the type-checker entirely, and defeats the purpose. Then, the JSON could have any data / shape whatsoever, with no errors provided.
2nd note: I can also sorta get there with satisfies Array<Omit<BookResource, 'type'> & { type: string }>
but that's kind of gross, and I want to enforce that the type truly is "book" and not just a string. JSON type inference is helpful but it isn't perfect, and currently there's no way to actual tell it how to interpret string literal types.