Repository navigation
Sane alternative to typeof | is | as | instanceof ? #28337
Description
Search Terms
typeguard type guard is instanceof instance of typeof typedefof as
Suggestion
All current type guards supported by TypeScript are substandard, full of "gotchas", and especially: extremely confusing for C# developers.
Use Cases
Problems with typeof: it's not strongly typed. It returns a string. Why not return a typed object so that you can compare objects instead of strings? This way you wouldn't need the dreaded/ugly triple-equal operator. (Sidenote: this is also super confusing for a C# developer since it works much more differently than C#'s 'typeof' keyword)
Problems with is: it needs to be used along with an auxiliar function. This is too verbose. Add a function for every type guard? If you have 3 type guards in a function, you need 4 functions! (Sidenote: this is also super confusing for a C# developer since it works much more differently than C#'s 'is' keyword)
Problems with instanceof: only works with constructors, doesn't work with primitive types. (Sidenote: the mere existence of this keyword makes C# devs confused because the difference of it is not clear from the 'is' keyword, and it doesn't seem to be a better version of is either, because it's only limited to a very specific constructor use case!)
Problems with as keyword: it's not a real dynamic cast, like in C#. See https://decembersoft.com/posts/typescript-vs-csharp-as-keyword/ (I still don't understand the purpose of as in typescript TBH.)
We need a new type guard that can get rid of all the problems of all use cases above.
My initial proposal would be "typedefof" or "isoftype" keywords, for which I put examples below (feel free to propose more keywords or approaches please).
Examples
let x: string = "hello";
if (typedefof x == typedefof string) {
...
}
let x: string = "hello";
if (x isoftype string) {
...
}
Key point of this feature: simplicity!
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. new expression-level syntax)
Activity
[-]Sane alternative to `typeof` | `is` | `instanceof` ?[/-][+]Sane alternative to `typeof` | `is` | `as` | `instanceof` ?[/+]DanielRosenwasser commentedon Nov 5, 2018
While I empathize with the many ways to do things in a slightly subpar way,
instanceofandtypeofare just the tools that ECMAScript gives us. Type predicates (signatures withx is Foo) are a way of communicating something about what a function is checking which is the closest way of overlaying erasable types on existing constructs.asis just a way of convincing the type system that something has some type; like you said, it's not a cast in the C# sense which is why it's called a "type assertion" rather than a "type cast".So this doesn't really seem like it's in the scope of the project since we're trying not to add anything with runtime impact that's not specified by ECMA262.
knocte commentedon Nov 5, 2018
Is the proposal I gave really a runtime-impact suggestion? I imagine
typedefofcould transpile totypeofunderneath.knocte commentedon Nov 12, 2018
Daniel Rosenwasser (@DanielRosenwasser) ping?^
superamadeus commentedon Nov 14, 2018
What you're asking for isn't really isn't possible. There would be many cases where the compiler wouldn't be able to know at compile time whether to use
typeoforinstanceofunder the hood, creating edge cases which are just as confusing as the current way of doing things.You can roll your own convenience type guard if you want.
Playground link (with usage examples)
spiritwindy commentedon Nov 19, 2018
superamadeus
isOfTypechecks the variable type, throws an exception if it does not matchDo not use
ifto determine whether you can infer the type ofveffect::
superamadeus commentedon Nov 20, 2018
@spitWind
I see what you're getting at. The pattern you're describing would work at runtime but there's no way to communicate your intentions (that you're asserting that type type of v must be "object") to the type system.
I'm trying to think of a situation where this is useful though. I understand using sanity checks to throw if types are invalid, but I can't think of a scenario where it would make sense to use a non-conditional type guard to infer the type of a value rather than typing it appropriately. Example how I would do what you described:
knocte commentedon Dec 6, 2018
Ryan Cavanaugh (@RyanCavanaugh) can someone answer comment #28337 (comment) before closing this at least?
superamadeus commentedon Dec 6, 2018
Andres G. Aragoneses (@knocte)
It wouldn't just transpile to
typeof. There would have to be a runtime decision whether or not to usetypeoforinstanceofbased on the values used. This is a runtime impact because you're adding a non-ECMA262 syntax change which changes the output code. This is against TypeScript's design goals.knocte commentedon Dec 7, 2018
I'm not advocating for a change! I'm advocating for a new operator.
DanielRosenwasser commentedon Dec 7, 2018
That new operator
Yes, that is code that has runtime impact.
superamadeus commentedon Dec 7, 2018
Andres G. Aragoneses (@knocte) Okay, as far as TypeScript is concerned, a new operator falls under the realm of "expression-level syntax" so that's out of scope.
knocte commentedon Dec 8, 2018
I never proposed
isOfTypeoperator to throw an exception, that was a comment from @spitWind . I'm advocating for a new type guard that is dynamic (in the same way the keywordasin C# is a dynamic cast, not a static cast).Can you confirm this Daniel Rosenwasser (@DanielRosenwasser) ?
YowaiCoder commentedon Dec 8, 2018
See TypeScript Design Goals.
Typescript is not C#, or maybe you can fork one, make it less restrained, and start to convince people to use it 😜
kitsonk commentedon Dec 10, 2018
Andres G. Aragoneses (@knocte)
Daniel already confirmed it with his point 2.
A runtime emit implies expression-level syntax.
knocte commentedon Dec 10, 2018
I don't see how a new operator that would transpile into existing javascript operators would have runtime impact.
superamadeus commentedon Dec 10, 2018
Andres G. Aragoneses (@knocte) It is an addition to the language to add a feature that is not in the ECMA262 specification, which changes the runtime code output of the transpiler. TypeScript tends to avoid adding expression-level syntax features which affect the runtime code output of transpilation unless it is a feature which is explicitly described in the ECMA262 specification.
It doesn't matter if it transpiles into existing JavaScript operators. If it's not an operator in JavaScript (ES2018), it's out of scope as a feature in TypeScript.
It is against TypeScript's goals to add features like new operators to the language unless they're defined in ECMA262