Real. I have to touch it sometimes at work and it's horrible. As soon as something touches a regular JavaScript API it becomes "any" and the type system stops helping you. It doesn't expect you to null check your nullable types. It's got a whole compilation process it could use to validate all sorts of things but it's worse at it's big reason for existing than Python 3 type hints are.
🤷 take it up with my web devs who chose to turn it off then. Idk why they did that. But if your ecosystem has people turning chunks of safety maybe the safeties aren't well done.
Like I said, I only touch it sometimes, but it really feels like lipstick on the pig of Javascript which I was similarly sometimes subjected to at a previous job. Typescript has not lived up to my hopes, I'm still jumping into debuggers to figure out what types things really are. Those types are better structured, which is good, but also sometimes they're lies because it's still just JavaScript underneath and too much of it leaks through the abstractions.
Typescript was designed with incremental adoption in mind, so the soundness of the type system depends on the degree of strictness you configure. If you disable strict mode and allow any all over the place then of course you’re going to have runtime bugs that could be avoided otherwise.
In other words… a skill issue being mistaken for a language deficiency.
This isn't at a boundary between TS and JS. This is a full blown TS project. And yes, maybe there's a skill issue aspect, but having used similar things like the annotations in Python 3 I feel like if the language motivates developers to turn off safety features those features are poorly designed.
"strongly typed" is the wrong phrase for the context - maybe "statically typed" would be better?
This. The proper term to describe Typescript is "statically typed".
"Strongly typed" is a completely vague phrase that means different things depending on the context. If I had to define it in few words, it'd be "unwilling to apply type conversions automatically", but other people will define it differently.
Javascript is often contrasted with Python, Python being strongly typed and Javascript being weakly typed. But outside of comparing similar languages, this phrase is mostly meaningless.
I'd argue that Typescript, being just a statically typed coat over Javascript, is still weakly typed, but I don't have strong opinions about it, as I simply don't use those terms, and I recommend no one does.
Here's some more info: https://stackoverflow.com/a/2696369/1648987
It betrays the loosey-goosey attitude that has overtaken the industry.
I'm sure that there are other such gems in the article like "types make invalid state unrepresentable", "types are comments/documentation that doesn't go out of date", or "typescript is just like javascript but the assumptions are made explicit".
So, "you think that Typescript can't be described as strongly typed due to design flaws"?
I can see that perspective. Though even after extensive experience with languages with more robust type checking - including Haskell and Rust - I still find Typescript's checking to be very helpful.
I think this article is a lot of noise over some information you would get with a more succinct and better presentation in typescript’s official handbook.
that 'universal tagged union' framing is so much better than how typescript is usually explained. like yeah typeof is just pattern matching on the runtime tags and structural typing only works cuz js already tagged everything
Currently you can only make the function a "configure it", even when it's not configurable in reality, it just depends on its parameters. There's a category of TS error that basically are "You can't do that because the function could be manually configured with something that wouldn't work".
Also I never managed to make this work as intended.
type Altered<O extends object, K extends string, T> = {
[P in keyof O]: P extends K ? T : O[P];
};
function appendProperty<K extends string, T, O extends object>(mapping: O, key: K, value: T): Altered<O, K, T>
{
const copy: Altered<O, K, T> = {
...mapping,
[key]: value,
};
return copy;
}
const result: { foo: string } = appendProperty({}, "foo", "bar");
But maybe it would work if we could write
function appendProperty(
mapping: infer O satisfies object,
key: infer K satisfies string,
value: infer T
): Altered<O, K, T>
{
const copy: Altered<O, K, T> = {
...mapping,
[key]: value,
};
return copy;
}
The issue of doing that is you'll be able to add arbitrary properties due to the index signature of Record. And any object that does not have that index signature won't be allowed as first parameter.
You could constrain it to any type you want in the function signature. Your example used object which allows arbitrary properties too. Do you want to pass an array? Is that what you’re saying?
If you put extends object instead of extends Record it won't work.
A variant is this
function createObject<T extends object, K extends keyof T>(key: K, value: T[K]): T {
return { [key]: value };
}
const result: { foo: string } = createObject("foo", "bar");
Because you cannot force the type of the key to be const. Someone could write this:
const result: { foo: string, bar: string } = createObject<{ foo: string, bar: string }, "foo" | "bar">("foo", "bar");
Which would be false, and thus the object creation has this error
Type '{ [key]: T[K]; }' is not assignable to type 'T'. '{ [key]: T[K]; }' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'object'.
That's why it only work with Record as the index signature allows arbitrary key presence.
Your example doesn't work with Record either because as your example shows you could pass a type with multiple key value pairs, but your function is only returning an object with one key in it. that is a valid error regardless of using object or Record. it's the type system being correct.
And yes this still works with object:
type Altered<O extends object, K extends keyof O, T> = Omit<O, K> & { [P in K]: T };
function appendProperty<O extends object, K extends keyof O, T>(obj: O, key: K, value: T): Altered<O, K, T> {
return { ...obj, [key]: value };
}
const result = appendProperty({ a: 'string' }, 'a', true);
result.a; // boolean
const result2 = appendProperty({ a: 'string' }, 'c', true); // error
Your example doesn't work with Record either because as your example shows you could pass a type with multiple key value pairs, but your function is only returning an object with one key in it.
Yes, that's why I posted it. To show it doesn't work.
But it's 5 in the morning and I haven't slept, so I think my messages are not really coherent and I should sleep.
Interesting! It looks to me like both of those would fix the argument and return value to the exact same type. Is it that you'd like an option that doesn't allow callers to override inference for the argument type with a type argument?
Yes, the idea is that it's not so much a "configure the function to your need" and instead a "returned type depends on argument type".
But you would still be able to "compute" types from the inference. For example
function values(map: Record<string, infer T>): Array<T>
Or reference previous arguments so you can even have dynamic typing
function apply(callable: infer F satisfies Function, args: Parameters<F>): ReturnType<F>
I would also like TypeScript being able to handle key/value pairs and constructors better.
Let's say I have a clone function and an object.
const new_mapping = { ...mapping };
for (const [key, value] of Object.entries(mapping)) {
if (some condition) {
new_mapping[key] = clone(value);
}
}
It will complain that "No index signature was found".
class Foo {
protected static bar: Bar = { ... };
public getSharedBar(): Bar {
const constructor: typeof Foo = this.constructor;
return constructor.bar;
}
}
This gives a superb "Type 'Function' is missing the following properties from type 'typeof Foo': bar".
That's a strong suit of TS though - you don't have to fill in all of these generic parameters. You could if you want - but it still won't let you break type safety if you do that. I'm not sure what you're saying with this comment.
> Typescript’s unions are unusual in that they are “untagged”.
They are tagged in the sense that the runtime knows whether any given value is a string, a number, an object, or one of the other foundational types, which is why TypeScript is the way it is. What the JavaScript runtime provides is sort of a 'universal tagged union' with a predefined list of tags.
This does happen to fit my brain pretty well, having spent a lot of time in dynamically-typed languages where 'of course the runtime has that information', but it does mean that there are sort of two classes of types: ones that can be distinguished at runtime, and ones that can't. If you only need the basic types, life is good. But if you want to distinguish between two objects that have the same structure, well, you can't. You need to add your own tagging system on top of those low-level objects. Which TypeScript handles incredibly well. But it does, I think, lead to some confusion about "structural types". You can only have "structural types" because the language is tagging all your values for you!
This is very different than the situation in, say, C, which doesn't provide runtime reflection (is this an int or a pointer to an array?), so if you want tagged unions you have to build the whole system from scratch.
Yeah, these are good points! Typescript exists in a weird spot, and it takes some understanding of how and why it got there to use it as effectively as possible.
safety-4th@reddit
with WASM i wouldn't dream of writing in JS or altJS when we have go and rust instead
pixelbart@reddit
Typescript is literally lipstick on a pig. I get the idea but I still don’t like it.
HighRelevancy@reddit
Real. I have to touch it sometimes at work and it's horrible. As soon as something touches a regular JavaScript API it becomes "any" and the type system stops helping you. It doesn't expect you to null check your nullable types. It's got a whole compilation process it could use to validate all sorts of things but it's worse at it's big reason for existing than Python 3 type hints are.
OHotDawnThisIsMyJawn@reddit
TS can do all those things, your TS setup is just bad
HighRelevancy@reddit
🤷 take it up with my web devs who chose to turn it off then. Idk why they did that. But if your ecosystem has people turning chunks of safety maybe the safeties aren't well done.
Like I said, I only touch it sometimes, but it really feels like lipstick on the pig of Javascript which I was similarly sometimes subjected to at a previous job. Typescript has not lived up to my hopes, I'm still jumping into debuggers to figure out what types things really are. Those types are better structured, which is good, but also sometimes they're lies because it's still just JavaScript underneath and too much of it leaks through the abstractions.
trappar@reddit
Typescript was designed with incremental adoption in mind, so the soundness of the type system depends on the degree of strictness you configure. If you disable strict mode and allow
anyall over the place then of course you’re going to have runtime bugs that could be avoided otherwise.In other words… a skill issue being mistaken for a language deficiency.
HighRelevancy@reddit
This isn't at a boundary between TS and JS. This is a full blown TS project. And yes, maybe there's a skill issue aspect, but having used similar things like the annotations in Python 3 I feel like if the language motivates developers to turn off safety features those features are poorly designed.
dhlowrents@reddit
10 days!
TedGetsSnickelfritz@reddit
If you have to fuck a pig, then wouldn’t you rather it wear lipstick?
Absolute_Enema@reddit
Stopped reading at "strongly typed".
TheWix@reddit
Why?
umtala@reddit
ADHD
hallettj@reddit (OP)
Not sure if you're interested in constructive feedback, but why is "strongly typed" an issue for you? I can think of a few possibilities:
Is it one of those? Something else?
vytah@reddit
This. The proper term to describe Typescript is "statically typed".
"Strongly typed" is a completely vague phrase that means different things depending on the context. If I had to define it in few words, it'd be "unwilling to apply type conversions automatically", but other people will define it differently.
Javascript is often contrasted with Python, Python being strongly typed and Javascript being weakly typed. But outside of comparing similar languages, this phrase is mostly meaningless.
I'd argue that Typescript, being just a statically typed coat over Javascript, is still weakly typed, but I don't have strong opinions about it, as I simply don't use those terms, and I recommend no one does.
Here's some more info: https://stackoverflow.com/a/2696369/1648987
hallettj@reddit (OP)
Makes sense; I appreciate the specific feedback!
Absolute_Enema@reddit
It betrays the loosey-goosey attitude that has overtaken the industry.
I'm sure that there are other such gems in the article like "types make invalid state unrepresentable", "types are comments/documentation that doesn't go out of date", or "typescript is just like javascript but the assumptions are made explicit".
leeuwerik@reddit
Yet again you couldn't make your point.
Absolute_Enema@reddit
You couldn't catch a fish in a barrel with that bait.
Rattle22@reddit
You need to become grug-brained.
EarlMarshal@reddit
And
hallettj@reddit (OP)
So, "you think that Typescript can't be described as strongly typed due to design flaws"?
I can see that perspective. Though even after extensive experience with languages with more robust type checking - including Haskell and Rust - I still find Typescript's checking to be very helpful.
EarlMarshal@reddit
It's mainly a linter.
DerelictMan@reddit
If they had a real point to make they would have already made it. Still, kudos for attempting to drag it out of them...
Merry-Lane@reddit
I think this article is a lot of noise over some information you would get with a more succinct and better presentation in typescript’s official handbook.
LevelIndependent672@reddit
that 'universal tagged union' framing is so much better than how typescript is usually explained. like yeah typeof is just pattern matching on the runtime tags and structural typing only works cuz js already tagged everything
hallettj@reddit (OP)
Thanks! I'm glad you liked it!
Blue_Moon_Lake@reddit
I wish
inferhad more use.function foo<T extends object>(arg: T): T { return arg; }let people choose
T, butfunction foo(arg: infer T satisfies object): T { return arg; }is closer to what I would like sometimes
Steveadoo@reddit
These seem like they would do the same thing to me. What am I missing?
Blue_Moon_Lake@reddit
Currently you can only make the function a "configure it", even when it's not configurable in reality, it just depends on its parameters. There's a category of TS error that basically are "You can't do that because the function could be manually configured with something that wouldn't work".
Also I never managed to make this work as intended.
But maybe it would work if we could write
Steveadoo@reddit
like this? this works.
Blue_Moon_Lake@reddit
The issue of doing that is you'll be able to add arbitrary properties due to the index signature of
Record. And any object that does not have that index signature won't be allowed as first parameter.Steveadoo@reddit
You could constrain it to any type you want in the function signature. Your example used object which allows arbitrary properties too. Do you want to pass an array? Is that what you’re saying?
Blue_Moon_Lake@reddit
If you put
extends objectinstead ofextends Recordit won't work.A variant is this
Because you cannot force the type of the key to be
const. Someone could write this:Which would be false, and thus the object creation has this error
That's why it only work with
Recordas the index signature allows arbitrary key presence.Steveadoo@reddit
Your example doesn't work with Record either because as your example shows you could pass a type with multiple key value pairs, but your function is only returning an object with one key in it. that is a valid error regardless of using object or Record. it's the type system being correct.
And yes this still works with object:
```
```
Blue_Moon_Lake@reddit
Yes, that's why I posted it. To show it doesn't work.
But it's 5 in the morning and I haven't slept, so I think my messages are not really coherent and I should sleep.
hallettj@reddit (OP)
Interesting! It looks to me like both of those would fix the argument and return value to the exact same type. Is it that you'd like an option that doesn't allow callers to override inference for the argument type with a type argument?
Blue_Moon_Lake@reddit
Yes, the idea is that it's not so much a "configure the function to your need" and instead a "returned type depends on argument type".
But you would still be able to "compute" types from the inference. For example
Or reference previous arguments so you can even have dynamic typing
I would also like TypeScript being able to handle key/value pairs and constructors better.
Let's say I have a
clonefunction and an object.It will complain that "No index signature was found".
This gives a superb "Type 'Function' is missing the following properties from type 'typeof Foo': bar".
Steveadoo@reddit
i feel like most of these already work. your first two examples:
Blue_Moon_Lake@reddit
Yes it's possible, but also you can alter the type by providing a value for T. That code rely on everything being implicit.
Steveadoo@reddit
That's a strong suit of TS though - you don't have to fill in all of these generic parameters. You could if you want - but it still won't let you break type safety if you do that. I'm not sure what you're saying with this comment.
TOGoS@reddit
> Typescript’s unions are unusual in that they are “untagged”.
They are tagged in the sense that the runtime knows whether any given value is a string, a number, an object, or one of the other foundational types, which is why TypeScript is the way it is. What the JavaScript runtime provides is sort of a 'universal tagged union' with a predefined list of tags.
This does happen to fit my brain pretty well, having spent a lot of time in dynamically-typed languages where 'of course the runtime has that information', but it does mean that there are sort of two classes of types: ones that can be distinguished at runtime, and ones that can't. If you only need the basic types, life is good. But if you want to distinguish between two objects that have the same structure, well, you can't. You need to add your own tagging system on top of those low-level objects. Which TypeScript handles incredibly well. But it does, I think, lead to some confusion about "structural types". You can only have "structural types" because the language is tagging all your values for you!
This is very different than the situation in, say, C, which doesn't provide runtime reflection (is this an int or a pointer to an array?), so if you want tagged unions you have to build the whole system from scratch.
hallettj@reddit (OP)
Yeah, these are good points! Typescript exists in a weird spot, and it takes some understanding of how and why it got there to use it as effectively as possible.