Description
π Search Terms
"satsifies", "satisfies generic parameter".
i found two similar/related feature requests, but they felt different enough to warrant being a separate feature request:
β 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 isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
β Suggestion
Currently, from what I can tell, is not possible to mimic the behaviour of satisfies
on a function's generic type parameter - i.e. to define a function with a generic parameter that allows inference of a literal type that conforms to that generic parameter, while still benefitting from excess property checking. Because the only option for a generic type parameter is extends
, there's no way (that I can see) to prevent extra keys in the inferred generic type.
π Motivating Example
A bare bones example of what this change would make possible:
type Foo = { a: number };
const foo = <T extends Foo>(x: T) => x;
foo({ a: 1, wrong: 2 }) // typechecks, but i don't want it to
foo({ a: 1, wrong: 2} satisfies Foo) // causes the type error i want
What I would really like to be able to write:
type Foo = { a: number };
const foo = <T satisfies Foo>(x: T) => x;
foo({ a: 1, wrong: 2 }) // causes the type error i want
π» Use Cases
- What do you want to use this for?
The reason I want this functionality is that I'm writing an ORM and would like to restrict the keys passed to the create
function to only the keys present in the model. To be clear I do not need a runtime check and any extra keys actually in the model are fine. The reason I want this feature is to provide a good experience when writing code like this with literal types:
class DB<Models extends BaseModels> {
public async create<
M extends ModelName<Models>,
P extends CreateParams<Models, M>,
>(m: M, params: P): Promise<CreateResult<Models, M, P>> {
// ...omitted for brevity
}
// ...omitted for brevity
}
db.create("post", {
data: {
title: "hello",
content: "it me",
authorId: uuid.v4(),
// @ts-expect-error field does not exist
wrong: 2,
},
returning: ["id"],
// this causes a type error if `satisfies` is used, but does not cause a type error if it is not
} satisfies CreateParams<typeof db.models, "post">);
- What shortcomings exist with current approaches?
It is only possible to use satisfies
at the call site of a function, and must be used at every call site, rather than being specified as part of the function's generic type parameter.
- What workarounds are you using in the meantime?
I have not been able to find a workaround. If there is one, I would be very happy to hear it! If not I think the only option is to use satisfies
at every call site.