2

Why does the following code pass the Typescript compiler?

type O = {
  name: string
  city: string
}

function returnString(s: string) {
  return s
}

let o1: O = {
  name: "Marc",
  city: "Paris",
  [returnString("random")]: "London",
}
Take
  • 324
  • 3
  • 10
  • 1
    looks like a compiler bug, maybe? [microsoft/TypeScript#22427](https://github.com/microsoft/TypeScript/issues/22427) seems like the exact issue but it was closed as "fixed" even though it sure doesn't seem fixed to me – jcalz Feb 21 '20 at 03:42
  • 3
    Okay I just opened [microsoft/TypeScript#36920](https://github.com/microsoft/TypeScript/issues/36920) and I'll come back to post info here if I get an answer as to what's happening. Good luck – jcalz Feb 21 '20 at 04:26
  • That's a good one ! (+1) – Maciej Sikora Feb 21 '20 at 08:37

2 Answers2

0

Why does this surprise you?

The code passes because, for one thing, it's valid plain-old JavaScript as of ES2015.

Even if it wasn't valid JavaScript, since this code can be translated to JavaScript that doesn't directly support computed key values, and it's a very nice, convenient thing to be able to do... why not support it?

As ES2015:

"use strict";
function returnString(s) {
    return s;
}
let o1 = {
    name: "Marc",
    city: "Paris",
    [returnString("random")]: "London",
};

Transpiled to ES5:

"use strict";
var _a;
function returnString(s) {
    return s;
}
var o1 = (_a = {
        name: "Marc",
        city: "Paris"
    },
    _a[returnString("random")] = "London",
    _a);
kshetline
  • 12,547
  • 4
  • 37
  • 73
  • 1
    Because it seems inconsistent to me for the reason that I could not enter `random: "London",` instead. – Take Feb 21 '20 at 03:46
  • 1
    This answer implies that valid JavaScript should not result in a TypeScript compiler warning, but [that's not the way it works](https://stackoverflow.com/a/52102877/2887218) (see also [this q&a](https://stackoverflow.com/questions/41750390/what-does-all-legal-javascript-is-legal-typescript-mean)) – jcalz Feb 21 '20 at 03:57
  • In particular, this question is asking why adding a computed property does not trigger [excess property warnings](https://www.typescriptlang.org/docs/handbook/interfaces.html#excess-property-checks). The answer cannot be "because it's valid ES2015". It *could* be "because the TS maintainers decided that this is idiomatic JS and shouldn't be warned", but that would need a documentation link or github issue link or something to back it up. Otherwise it's just conjecture/opinion. – jcalz Feb 21 '20 at 04:01
0

It's because of the : never type. I believe this is a very advanced typescript concept which is usually used to discriminate unions.

This is not a complete answer but it could serve you like a hint to understand the problem, it loses the scope of the string return value.

type O = {
  name: string
  city: string
}

declare const neverValue: never;

// This will fail
// function returnStringS(s: string): 's' {
//     return 's';
// }

// This will work
function returnStringS(s: string): string {
  return 's';
}

// This will work
function returnStringS(s: string): string {
  return s;
}


let o1: O = {
  name: "Marc",
  city: "Paris",
  [returnStringS("random")]: "London",
}

let o2: O = {
  name: "Marc",
  city: "Paris",
  [returnString("random")]: "London",
}

const some2: O = {
  name: "Marc",
  city: "Paris",
  [neverValue]: "London"
}

At the Typescript documentation TS never type is the key of this issue/concept.

The never type is a subtype of, and assignable to, every type; however, no type is a subtype of, or assignable to, never (except never itself). Even any isn’t assignable to never.

costadvl
  • 128
  • 2
  • 12
  • There's no `never` type here. The question's `returnString()` returns `string`, not `never`. It isn't *annotated* to return `string`, but the compiler infers it anyway. So this answer doesn't seem relevant, unless I'm missing something. Can you elaborate? – jcalz Feb 21 '20 at 15:18
  • The point is that the `: never` type is the only type that can be used to extend a defined size interface or type, and since it is assignable to every type (as written in the documentation) such as `: string` it could explain or at least serve as a hint why it accepts a `: string` but not a `: 's'` defined string. – costadvl Feb 24 '20 at 09:49