#How to use a const asserted object both as const and as widened object

1 messages · Page 1 of 1 (latest)

tranquil skyBOT
#
tickleme_pink#0

Preview:ts //I have constant data of the form // type Data = { // [string]: { // field1: string; // field2: string; // } //} //Where the key is the identifier for the object. //I'd like to keep const assertion while avoiding duplication of the id and return ...

iron pelican
#

you should probably better describe what type you are expecting as a result, instead of explaining what you think the solution is/should be

#

so you want to go from

type Data = {
  "someString": {
    field1: "field1";
    field2: "field2";
  };
  "someOtherString": {
    field1: "field3";
    field2: "field4";
  };
};

to

type Result = SomeTransformation<"someString">; // { id: "someString", field1: "field1", field2: "field2" }
#

right?

#

@sonic laurel

tranquil skyBOT
#
ascor8522#0

Preview:```ts
type ValidKey = ${string}Key;

interface Foo {
someValue: number;
}

type Bar = Record<ValidKey, Foo>;

type Baz<T extends Bar, K extends ValidKey> = K extends keyof T
? { id: K } & T[K]
: ({ id: K } & Foo) | undefined;

const myBar = {
someKey: {
...```

iron pelican
#

also, are you sure about the "validKey" case that should return { id: "validKey", somevalue: number } | undefined and not undefined
since your code simply returns undefined every time

sonic laurel
#

the "validKey" is for usage that isn't static, I just return undefined but the key is just dynamic so it may match the record.

#

I forgot to mention that what I was after is a 100% safe way to do this. If using as I already can do that with my approach.

sleek sigil
#

you can't do a conditional return type 100% safely in the current version of typescript

#

you either need assertions or overload signatures, both of which are unsafe

#

your question suggests conditional return because of how you want getData to return a different type depending on its input. there might be another way to go though, haven't looked that closely yet

sleek sigil
#

took a closer look. i think this satisfies all of your requirements, but it requires mentioning each valid key in getData (as i suspect all possible assertion/overload-less solutions will):

tranquil skyBOT
#
mkantor#0

Preview:```ts
...
type Return<K extends ValidKey> = ({
[K in keyof X]: {
id: K;
somevalue: X[K]["somevalue"];
}
} & {
[key: ValidKey]: {
id: K;
somevalue: number;
} | undefined
})[K];

export function getData<K extends ValidKey>(k: K): Return<K> {
const widenedK: ValidKey = k;
switch (widenedK) {
case 'somekey':
case 'otherkey':
return {id: k, ...x[widenedK]};
default:
return undefined;
}
}
...```

sleek sigil
#

note that this also relies on a subtle unsoundness in the type system though. for example this buggy version doesn't have compile-time errors but lies about what it returns at runtime for valid keys:

export function getData<K extends ValidKey>(k: K): Return<K> {
  const widenedK: ValidKey = 'definitelynotexistingkey'; // oops
  switch (widenedK) {
    case 'somekey':
    case 'otherkey':
      return {id: k, ...x[widenedK]};
    default:
      return undefined;
  }
}
#

still probably better than overloads

#

not using a spread in the return value makes things ever-so-slightly safer: return {id: k, somevalue: x[widenedK].somevalue} (just in case a rogue excess property named id exists within x)

sonic laurel
#

I'd be dealing with many more keys here so listing them doesn't really work. I'm actually surprised that a check if (k in x) doesn't narrow it to one of the keys.

sleek sigil
#

x in y narrows y, not x

#

what's your use case for this function?

sonic laurel
#

I have a set of static accounts for different uses and I have validation on the format of the name and such. I want to be able to refer to accounts directly but there's also a use case where one can retrieve the data based on requirements which would not be const.

#

I like const asserted stuff because it makes the development experience much better and I just wanted to prevent the other use-case from affecting that.

sleek sigil
#

there's also a use case where one can retrieve the data based on requirements which would not be const.
can you use a different function for this? the awkwardness comes entirely from the fact that you're "overloading" (in spirit) getData to do a different thing for the two cases

sonic laurel
#

The main issue was that I couldn't do mydata[key] because key is wider than the const asserted keys.

sleek sigil
tranquil skyBOT
#
mkantor#0

Preview:```ts
...
type SpecificReturn<K extends keyof X> = {
id: K;
somevalue: X[K]["somevalue"];
}
export function getSpecificData<K extends keyof X>(k: K): SpecificReturn<K> {
return {
id: k,
somevalue: x[k].somevalue,
}
}

type NonspecificReturn<K extends ValidKey> = undefined | {
id: K;
somevalue: number;
};
export function getNonspecificData<K extends ValidKey>(k: K): NonspecificReturn<K> {
const widenedX: ValidX = x
return (k in x) ? ({
id: k,
somevalue: widenedX[k].somevalue,
}) : undefined
...```

sonic laurel
#

Yea I really didn't think of just assigning it to the widened type, I was casting the key instead and didn't like that