Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The like operator - type-checking without typing #47834

Closed
cefn opened this issue Feb 10, 2022 · 3 comments
Closed

The like operator - type-checking without typing #47834

cefn opened this issue Feb 10, 2022 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@cefn
Copy link

cefn commented Feb 10, 2022

Suggestion

A like operator should be introduced which occupies the same position in the AST of e.g. as or as const relative to the declaration of a value, but only introduces validation against a type (as if it was being assigned to that type) without changing the actual type of the value, especially without broadening it.

🔍 Search Terms

Type-checking without Type-casting.
Inference-preserving type-check.
Type Validation

I have shared some examples for the suggestion below, but I probably just haven't found the right terms for what I'm looking for and it's a request which has already been considered. If this is the case I would like to find the issue and track the feature.

✅ Viability Checklist

This is deliberately designed to follow the existing type-erasure approach and introduces no new forms of checking, but just hints to the compiler some extra checking you would like to error on. In particular, you want all the typing of the existing assignment, but with the further type-checking of an additional type.

  • [✅ ] 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

I suggest that a like operator is introduced which triggers the type-checking normally associated with assignment but which does no type-casting (e.g. the type actually assigned is not affected by adding like MyType).

📃 Motivating Example

Given a type like this...

interface LogItem {
  kind?:"error" | "warn" | "info"
} & ({msg:string} | {message:string});

It might be used in scenarios such as this, ensuring that compile-time typechecking doesn't drive the creation of unnecessary runtime code...

Inline type-checking

Existing common pattern

Send back a JSON response in an express server (via an untyped method)

res.json({
  kind:"error"
  msg:"It all went to heck"
}); 

Workaround : unnecessary runtime assignment

const item: LogItem = {
  kind:"error"
  msg:getMessage(),
};
res.send(200).json(item);

Using like : avoids runtime assignment

res.json({
  kind:"error"
  msg:"It all went to heck"
} like LogItem); 

...and would raise errors for the following obvious cases...

res.json({
  kind:"error"
  stupid: "This property is excess",
  msg:"It all went to heck"
} like LogItem); 
res.json({
  kind:"error"
  // missing msg or message
} like LogItem); 

Inference-preserving type-checking

Given the standard workaround we get unnecessary type-broadening

const item: LogItem = {
  kind:"error"
  msg:getMessage(),
};
res.send(200).json(item);
// unnecessary compiler error since msg is not known to be set
// we can't benefit from `as const` at the same time as typechecking
if(item.msg.match(/specialsystem/)){
  notifyOnCallPager(item);
}

We can make sure the type has msg by using as const and use an extra throwaway runtime assignment in a block scope to do the validation, but it does nothing at runtime and is weird...

const item = {
  kind:"error",
  msg:"It all went to heck",
} as const;
{
    const ensureItem:LogItem = item;
}

This also introduces potential problems where there are no excess property checks, meaning this compiles when the developer probably doesn't want it to.

const item = {
  kind:"error",
  msg:"It all went to heck",
  stupid:"this should not be here"
} as const;
{
    const ensureItem:LogItem = item;
}

With the like operator, the item will be validated as a LogItem but can still have the known literal types it gets from as const. It hasn't been unnecessarily broadened to LogItem where msg is optional, simply as part of trying to validate its shape, and like will report on excess properties.

This would compile and fulfil the requirements by using like

const item = {
  kind:"error",
  msg:"It all went to heck",
} as const like LogItem;
res.send(200).json(item);
if(item.msg.match(/specialsystem/)){
  notifyOnCallPager(item);
}

And this would error on stupid as an excess property.

const item = {
  kind:"error",
  stupid:"this should not be here"
  msg:"It all went to heck",
} as const like LogItem;

Potentially order could matter, so that if LogItem does not accept readonly properties, you could reorder the operators, meaning LogItem gets to do the validation before it gets typed to readonly.

const item = {
  kind:"error",
  stupid:"this should not be here"
  msg:"It all went to heck",
} like LogItem as const;
@MartinJohns
Copy link
Contributor

MartinJohns commented Feb 10, 2022

It sounds like you want #7481, which is already scheduled for TypeScript 4.6. 🎉

@cefn
Copy link
Author

cefn commented Feb 10, 2022

Dead right, even the same example... #7481 (comment)

...so what remains I guess is an operator-naming suggestion, given like is shorter than satisfies :)

@MartinJohns
Copy link
Contributor

There's still some discussions going on, but I think a chance to change the name has sailed. I don't really like it either, but I have to admit that it's much more expressive than like. 🤷

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Feb 10, 2022
@cefn cefn closed this as completed Feb 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants