Hacker News new | ask | show | jobs
by 323 1373 days ago
One think I struggled a lot with until I got it is that the TypeScript types and the JavaScript code live in totally separate universes, and you cannot cross from the type world to JavaScript values because the types are erased when transpilling - meaning they can't leave any trace.

This means that it's impossible to write this function:

    function isStringType<T>(): boolean { return ... }

    const IS_STRING: boolean = isStringType<string>();
At best you can do something like this, which is inconvenient for more complex cases:

    function isStringType<T, IsString extends boolean = T extends string ? true : false>(isString: IsString): boolean { return isString }

    const IS_STRING_1: boolean = isStringType<string>(true); // compiles
    const IS_STRING_2: boolean = isStringType<string>(false); // type error
You basically need to pass the actual result that you want in and just get a type error if you pass in the wrong one. Still better than nothing.

Link if you want to play with it online: https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABDA...

Put another way, you can't do reflection with TypeScript.

You can write that function in C++ templates, and I naively assumed that it's possible in TypeScript too, since from my observations TypeScript allows complex typing to be expressed easier in general than C++.

5 comments

I think that’s actually a positive feature of TypeScript -- a useful limitation. Reflection is generally a bad idea and it’s good to be forced to do without it.

It is a bit annoying sometimes that you can’t have overloaded functions with different types, but in that case you can usually just give the overloads different names, and usually that’s better for readability anyway. (Or if you really want to, write one function and use JS reflection to do the overloading manually) (but you really don’t!)

Here’s an interesting discussion of the overloading question in Swift: https://belkadan.com/blog/2021/08/Swift-Regret-Type-based-Ov...

> Reflection is generally a bad idea

it is not - dynamic reflection certainly has its issues, but static reflection is absolutely fine

> it’s good to be forced to do without it.

it just leads to people reinventing it even more badly with separate tools

You’re right, I was just thinking about dynamic reflection.

My concern is with fiddly runtime stuff like “instanceof” -- I find that can go wrong in surprising ways. Better to just trust values to implement the interface they say they implement rather than trying to forcibly cast them.

Wanted to note that you can do function signature overloading in typescript- you just have to have a single function at the bottom that encapsulates all the different signatures and then branches its logic dynamically based on the values it's given: https://stackoverflow.com/questions/13212625/typescript-func...

I actually think this is a super cool and elegant way to do overloading

How do you give the overloads different names? Can you give a little example?
I’m just thinking of separate functions like this, which you can do in many languages:

add(n: number)

add(s: string)

In TypeScript (and also in C and Objective-C) you need to give them different names:

addNumber(n: number)

addString(s: string)

But see also brundolf’s reply -- if you have a single function that happens to take multiple overloads, TS does let you declare each overload; but it still needs a single implementation (likely with some runtime dispatching) in that case. I haven’t used that much myself, but maybe I should give it a go!

You might be interested in DeepKit[0]. In short, it enables introspection/reflection of typescript types at runtime, and builds off of that to do super interesting things like an ORM, an API framework, ... etc.

[0] https://deepkit.io/

Thanks, their @deepkit/type is exactly what I would need, but it seems they do that by a TypeScript plugin, and I'm in an esbuild setup which completely bypasses TypeScript.

But I will check if maybe I can use DeepKit to auto-generate files with the reflection info I need as a separate build step.

You can use type guards for something similar: https://www.typescriptlang.org/docs/handbook/advanced-types....
What they were getting at is that you can't observe T itself, only values passed in as type T
Keyword similar. I use type guards for other things (like typing JSON responses), but they can't solve this particular problem.
Does TypeScript support dynamic dispatch? JS by nature is dynamically typed you should be able to introspect at get the type at runtime.
That boundary is why I have a hard time taking TypeScript seriously. A type system that doesn't participate in code generation is what.. just for linting and documentation basically? Is that what we are become? Is that all people think a type system is good for?

Worse there is one value that is both a user-definable TypeScript type and a JS value.

> That boundary is why I have a hard time taking TypeScript seriously. A type system that doesn't participate in code generation is what

How is being able to check code correctness at compile-time even close to "just linting and documentation basically"? This has to be a bad faith argument

> Worse there is one value that is both a user-definable TypeScript type and a JS value.

What does this even mean? I don't think you understand TypeScript.

It's just fancy documentation. It doesn't contribute to behavior at all. Typescript can describe polymorphism, lamely, but it can't drive it.
> It doesn't contribute to behavior at all

I agree that TypeScript, in and of itself, doesn't change the way the code executes. This is self-evident.

But the idea that that makes it simply "fancy documentation" is hilarious. I have never seen documentation that can tell you at compile-time how your code will behave, it's fundamentally stupid to argue that static type checking is in any way comparable with documentation