#Computed Property Name Typing

19 messages · Page 1 of 1 (latest)

finite anvil
#

I'm trying to build a constructor which allows you to pick the name of one key:

function func<T>(arg: T, name: string) {
    return {
        [name]: arg, 
        foo: true,
        bar: '...',
        baz: 23
    }
}
const { hey } = func('yo', 'hey');

As you can see in this REPL, the resulting custom property's type get's mixed up with all the other types in the result object, i.e. it's string | number | boolean.

Why does this happen? How can I avoid the issue?

wicked bloomBOT
#

@finite anvil Here's a shortened URL of your playground link! You can remove the full link from your message.

ixxie#0

Preview:```ts
function func<T>(arg: T, name: string) {
return {
[name]: arg,
foo: true,
bar: "...",
baz: 23,
}
}

const {hey} = func("yo", "hey")```

hot scaffold
#

!*:known-and-unknown-keys

wicked bloomBOT
#
retsam19#0
`!retsam19:known-and-unknown-keys`:

TS doesn't work well with objects that mix "known keys" of one type onto an object with unknown keys (i.e. an index signature) for a different type.

type DoesNotCompile = {
    [key: string]: number; // all unknown keys are numbers
    specificKey: string; // but this key is a string
}

You can 'fake' it with intersections:

type Compiles = { [key: string]: number;}
  & { specificKey: string };

but this type is essentially read-only. You can read from it as you'd expect, but you can't assign anything to this type (without an assertion).

If your API isn't set in stone, it's recommended to put the unknown keys in their own object:

type Idiomatic ={
   specificKey: string;
   unknownKeys: Record<string, number>;
}
hot scaffold
#

@finite anvil I think this is a variant of 👆

finite anvil
#

hmmm

#

thanks for the info

hot scaffold
#

The index signature has to include all the possible values, and this can be relevant because you could do func('foo', 'hey')

finite anvil
#

I'm not sure if I can get way with the second solution

hot scaffold
#

Yeah, there's a different fix here, you can avoid 'unknown' keys entirely with a generic:

#
function func<T, K extends string>(arg: T, name: K) {
    const namePart = { [name]: arg } as Record<K, T>;
    return {
        ...namePart, 
        foo: true,
        bar: '...',
        baz: 23
    }
}

const { hey } = func('yo', 'hey');
wicked bloomBOT
hot scaffold
#

Technically, this isn't completely safe (hence the as Record<K, T> assertion) - it's possible that code could be written like:

const res = func<'X' | 'Y'>('yo', 'X');
//  According to the type, res has both `X` and `Y` keys, but in reality it only has `X`.
#

But realistically, I think it's safe enough.

finite anvil
#

hmmmmm

#

That's for the illumination @hot scaffold

#

I think when I account for how weird this is, I think I will try to avoid it

#

In that, its a bit unsafe in the sense that it can confuse collaborators 😄

hot scaffold
#

Yeah, if this isn't simplified, I'd just do the spread yourself.