Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I'm glad you asked. :-) For a start, number types. I want i32, i64, u32 etc. JavaScript (and therefore TypeScript) only has "number". Yes we now finally have BigInt but it's not ideal for JIT optimisation.

Object prototypes is a weird part of JS that we really could do without. If you really want that, use class syntax.

The Date object. Need I say more...

Little things like Object.keys() should return a (keyof T)[] rather than a string[] but can't due to JS edge cases.

Want first class tuples/immutable arrays.

Many other new syntaxes that can't be implemented due to the need for JS compatibility.

I'm sure there are others that I can't think of right now...



Object.keys() is always frustrating.

Also, the array access not returning an optional type (I wish there was a strict option for that) often gets me.

We need a middle ground: “TypeStrict“ that can clean up some of the edge cases.

If you need strict integers, you can create your own fake type:

type Int32 = number & {__type:'Int32'};

I do this with strings sometime when I want a string subtype that can’t be accidentally assigned by a normal string.


In TS 4.1+ there _is_ an option for strict array access called `noUncheckedIndexedAccess` [1]

[1]: https://devblogs.microsoft.com/typescript/announcing-typescr...


Awesome!

I was just searching for that and all I could find were old github issues saying it was working as intended.

Thank you!


Good news, safe array access is coming in TS 4.1

https://devblogs.microsoft.com/typescript/announcing-typescr...


For anybody interested in this technique: the googleable term is "branding". It's bringing some nominal typing into TS's structural typing


I wrote an article about this a while ago: https://codemix.com/opaque-types-in-javascript


Cool, I never knew what that was called. Thanks!


For new code, I think most JS devs have already switched to using the class syntax. Also FP in JS is alive and well if you want to avoid dealing with prototype chains altogether.

I think we can all agree the Date API sucks, but life can still be good if you just give in and include a date library. Also, there's some light at the end of the tunnel with Temporal proposal coming along [1].

Object.keys returning string[] is purely a TypeScript design decision, coming from how TypeScript chooses to model object subtyping [2].

  type Foo = {
    a: string
  }
  
  const keysOfFoo = (obj: Foo) => Object.keys(obj)
  
  const foo = {a: '', b: 5}
  keysOfFoo(foo)
The fact that this passes type checks is a conscious TS design decision that comes with both advantages and disadvantages. It wouldn't have to be this way; Exact types [3] could potentially be used to describe that if the type is exactly Foo, it's safe to assume Object.keys(foo) is (keyof Foo)[].

For first class tuples (and records), there's a stage 2 EcmaScript proposal coming along [4]. There's of course also the readonly [] type in TypeScript if you only need the safety of not accidentally pushing to an "immutable" array. First class language support could have nice additional features though, like strict equality.

Anyways, if these are the things you don't like about JS/TS, I don't think AssemblyScript will be the answer for you, as the goals of that language seem to be entirely different from "fixing" old JS cruft. Apart from the i32, i64 stuff I guess.

[1] https://github.com/tc39/proposal-temporal

[2] https://github.com/Microsoft/TypeScript/pull/12253#issuecomm...

[3] https://github.com/microsoft/TypeScript/issues/12936

[4] https://github.com/tc39/proposal-record-tuple


> I don't think AssemblyScript will be the answer for you, as the goals of that language seem to be entirely different from "fixing" old JS cruft. Apart from the i32, i64 stuff I guess.

Basically AssemblyScript try to fix JS runtime as well. See this: https://twitter.com/AssemblyScript/status/114604718874680934... and https://twitter.com/AssemblyScript/status/114466636945316659...


That pull request from 4 years ago with regards to Object.keys is worth reading if you already or are considering writing a “type wrapper” for Object.keys, especially now that better tuple types have landed and string template types are coming.

Runtime types can still get you there. I’ve been happy with myzod for this purpose, which feels like writing TypeScript while getting you everything you need to validate at runtime in a very performant package.


Another potential benefit - runtime metaprogramming. All types are erased when converted to JS, but I can imagine a lot of utility for runtime types in Typescript.


Obligated mention of runtypes (https://github.com/pelotom/runtypes) that bridges the gap between type-land and runtime-land. It's not seamless at all but if you have a serious need for this sort of thing, it's great


Not quite the same, but reminds me a bit of yup (https://github.com/jquense/yup)!


I think there is some experimental decorator syntax which will allow for extracting type info. Does runtypes interop with that? Or is planning to?


there's the reflect-metadata package but the type information it exposes is very limited, "object" instead of {a: "foo"} and so on.


Why not use a language that has the benefits of TS + everything you say and is designed to be independent from JS.

I'm thinking of OCaml for example. Why not use OCaml rather than TS?

Edit: Actually the idea is so obvious that you have ReasonML that can compile both to JS and assembly and is basically OCaml with a JS-like syntax: https://reasonml.github.io/docs/en/what-and-why

Isn't that exactly what you're thinking of?


OCaml doesn't have all the benefits of TS, when it comes to the power and the flexibility of its type system.


Little things like Object.keys() should return a (keyof T)[] rather than a string[] but can't due to JS edge cases.

This was something that confused me when I first started learning Typescript but is actually not an edge case but a fundamental feature of Typescript's structural typing.

A type T is only guaranteed to be at least (a subtype of) T. Which in the case of objects means it has at least those fields, but potentially more. For instance

  interface FooBar {
    foo: string
    bar: string
  }

  interface Baz {
    baz: number
  }

  const fooBarBaz: FooBar & Baz = {
    foo: 'foo',
    bar: 'bar',
    baz: 3
  }

  
  const printFoobar = (fooBar: FooBar) => {
    for (const key of Object.keys(foobar)) { 
      if (key !== 'foo' && key !== 'bar') {
        // If Object.keys was (keyof T)[] Typescript would claim this branch is unreachable
      } 
    }
  }

  printFooBar(fooBarBaz)
It's really worth thinking about Typescript types not as nominal objects like in Java or Haskell, but contracts about minimum functionality. Embracing this in terms of function arguments and return types makes testing and composing typescript code much easier. For example, if you were writing a AWS Lambda function in Typescript that only uses the body of the incoming event.

  export const handlerOne = (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
    console.log(event.body)

    return {
      statusCode: 200,
      body: event.body,
    }
  }

  interface MinimalEvent extends Pick<APIGatewayProxyEvent, 'body'> {}

  export const handlerTwo = (event: MinimalEvent): Promise<APIGatewayProxyResult> => {
    console.log(event.body)

    return {
      statusCode: 200,
      body: event.body,
    }
  }
handlerTwo only requires a handlerTwo({ body: '....' }) call in a test, instead of having to fill in all the extra properties of the APIGatewayProxyEvent that aren't even required. You might be tempted to use a type coercion in the test instead e.g. handlerOne({ body: '....' } as APIGatewayProxyEvent) but the problem with the coercion is that is in not checked at all by the compiler, it's essentially like temporarily using an any type. So if handlerOne is updated in the future to depend on more of the structure of APIGatewayProxyEvent Typescript will be none the wiser that the test requires updating (although granted hopefully the test would fail).

Anyway, that's a long winded explanation of why Object.keys shouldn't return (keyof T)[]. If you want to program generically over the Type of something I'd suggest making use of a runtime type library like io-ts instead which gives you a data structure representing the type to iterate over, etc.


> Want first class tuples/immutable arrays.

On this subject specifically, see the records & tuples proposal (currently at stage 2): https://github.com/tc39/proposal-record-tuple


When you say doing without object prototypes -- are you advocating moving away from the entire prototypal inheritance model? Or just not exposing it?

Your requirements list makes me wonder why you want to stick with anything related to JS at all? With all the transpiling options, web assembly, etc.


What are the edge cases for Object.keys()? I've always wondered why that returned string[]


Example:

  const x = { a: 1, b: 2, c: 3 }
  const y: { a: number, b: number } = x
  console.log(Object.keys(y))


Why not make TS compile to LLVM?




Consider applying for YC's Winter 2027 batch! Applications are open till November 2.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: