TypeScript 2.4
blogs.msdn.microsoft.com
blogs.msdn.microsoft.com
It's a source of mystery that they didn't realise that was going to be a problem right from the start.
> TypeScript 2.4 now tightens up how it checks two function types by enforcing the correct directionality on callback parameter type checks.
I understand what they mean after quite a bit of wrestling with that and the example, but I feel like that could have been far better worded for laypeople.
It starts here:
https://blogs.msdn.microsoft.com/ericlippert/2007/10/16/cova...
You can follow along further through the tag:
https://blogs.msdn.microsoft.com/ericlippert/tag/covariance-...
The source can push anything into the pipe that is a dog or more specialized than a dog, ie they can push in specific dog breeds like only terriers. So the source can treat the "Pipe of Dog" as a "Pipe of Terrier". It's okay because they're dogs and the pipe accepts dogs. The source cannot push in any animal that isn't a dog.
When the sink pulls something out of the pipe, they can expect to receive anything that's a dog or less specialized than a dog, eg they can expect that everything they receive will be an animal. So the sink can treat the "Pipe of Dog" as a "Pipe of Animal". The sink can expect not to receive anything that isn't an animal. The sink can't expect that they'll only receive terriers, since the source may decide to push bulldogs instead.
The source end of the pipe is contravariant on Dog. The sink end of the pipe is covariant on Dog.
Edit: To bridge this with programming: A function is a pipe. The input of the function is the source. The output of the function is the sink. A function that accepts a Dog could be given a Terrier, and a function that returns a Dog can be considered to return an Animal.
Data structures are equivalent to functions in this respect. A data structure that produces instances of Dog is the same as a function that produces a Dog - IEnumerable<Dog> in C# is also an IEnumerable<Animal>, Promise<Dog> in JavaScript is also a Promise<Animal>. A data structure that both accepts and returns instances of Dog is invariant on Dog - List<Dog> in C# can't be List<Animal>, Array<Dog> in JavaScript can't be Array<Animal>, because that would let someone push Cats into them.
Edit 2: By the same reasoning, a callback parameter flips the input-output-variance association for every level of nesting. For a first-level callback, the inputs to the callback are produced by the function, so the input of the callback is equivalent to an output of the function, and the output of the callback is equivalent to an input of the function. Eg consider a JS function `function foo(x: (y: A) => B): C { }` This is covariant on A, contravariant on B, covariant on C.
Now suppose you have a special pipe that when you push a dog into it, a cat comes out on the other end. This is called a dog-to-cat pipe. If you stick a dog-to-cat pipe to the end of a pipe of dog, then you get a pipe of cat (from the point of view of the sink end). The transformation varies with the direction of the dog-to-cat pipe. Hence covariant.
Now, what if you have a cat-to-dog pipe and stick it to the source end of a pipe of dog? Then you have a pipe of cat, from the source's point of view. The transformation varies against the direction of the cat-to-dog-pipe. Hence contravariant.
The example in the parent comment is with the more realistic example of a terrier-to-dog pipe or a dog-to-animal pipe. Of course, a pipe of dog is just a special name for a dog-to-dog pipe.
(Where this gets somewhat more complicated is when you have pipes which take pipes.)
A function `X -> string` can be turned into a function `X -> object`: call it, take its string result and cast it to object. This is called covariant (latin co- for "together") because casting `string` to `object` also lets you cast `X -> string` to `X -> object`.
A function `object -> X` can turned into a function `string -> X`: take the string argument, cast it to object, and pass it to the function. This is called contravariant (latin contra- for "against") because casting `string` to `object` lets you cast `object -> X` to `string -> X`.
This is exciting. About a month ago, I tried this just expecting it to already be supported. I was sad to learn it wasn't.
Kudos to the Typescript team for the awesome, continual improvements.
Dynamic imports is pretty huge too, that should come in handy for a lot of people.
[1]: https://medium.com/@thebosz/creating-a-dart-to-javascript-in...
For example.
enum Test { a } let a = Test.a; let b = Test[ 0 ]; // isn't available on the string ones
I wonder why its like that.
You could have a situation where you have a string as a code, and then the display name for example, and you can't really do that as is now. This seems more useful than checking for the type (which in a numeric enum is either a number or string, so you would still need to do some work to properly validate it).
var Test;
(function (Test) {
Test[Test["a"] = "RED"] = "a";
})(Test || (Test = {}));
While 2.4 doesn't error out, but omits the inner assignment, thus not allowing the lookup. var Test;
(function (Test) {
Test["a"] = "RED";
})(Test || (Test = {}));Of course, it does look like this release has no? Salsa-related fixes or improvements, so it doesn't surprise me.
This language is getting better and better with each release
I'd recommend doing a similar path; transitioning existing code to TS is IMO a better way to learn it than from scratch. TypeScript Deep Dive is a great resource.
If you want the full immersion, then read the entire Handbook: https://www.typescriptlang.org/docs/handbook/basic-types.htm....
TypeScript 2.3 has support for vue.js this injection style patterns (see https://github.com/Microsoft/TypeScript/pull/14141) which are way out of the realm of class-centric programming languages. Similarly the inference cabalities (expanded in 2.4) and the fundamental nature of the type system being structural rather than nominal is another give away of the language nature and direction.
So the short answer is TypeScript is a superset of JavaScript; and is as class-centric as JavaScript is.
I kind of feel like this is when everybody converted codebases to CoffeeScript and are now stuck with having to undo that...
Yes, I get strong typing has merits - but so does writing the native language.
Naturally. But the merits of strong typing far outweigh those of writing the native language, for me at least. If you have a project of any reasonable size, TypeScript is such a huge productivity boost it's incredible.
I was very sceptical (having indeed been burnt by trying CoffeeScript before) but the comparison doesn't really work with TypeScript. If it died tomorrow and we all decided to go without it, we'd just transpile to JS and be done with it - CS -> JS looks very, very different, but TS -> JS is the exact same code, just with types stripped out.
But even if all of that does go away, types outweigh writing without types imo - but you can always take the output of TSC and stop using it going forward, its not obfuscated.
Big difference is that ES 6 has indeed replicated most solutions of CoffeeScript. However so far I can tell ES won't be ever(?) statically typed and therefore it won't ever(?) solve same problem space as TS.
That said, you would expect more of a middle ground like Python where Typescript type annotations become syntactically valid in ECMAScript but with no clear semantics at runtime, and Typescript would still be needed to compile/verify type assertions.
There's also the case that transpilers will likely remain test beds for both future features for the language and supporting complicated backwards compatible scenarios.
interface Ok<T> { kind: "ok"; value: T; }
interface Err { kind: "err"; error: string; }
type Result<T> = Ok<T> | Err
const maybeNumbers: Result<number>[] = [
{ kind: "ok", value: 10 },
{ kind: "err", error: 'some error' },
{ kind: "ok", value: 9 },
];
function isOk<T>(thing: Result<T>): thing is Ok<T> {
return thing.kind === "ok";
}
const okNumbers: Ok<number>[] = maybeNumbers.filter(isOk);