#single named key in object with other keys having a different type

22 messages · Page 1 of 1 (latest)

novel bane
#

I'm working with a library that has kind of a weird object signature for its values, for simplicity's sake i've made a derived example..

i'm trying to get the values of the object to all have number as the type, while the single key is something that has a named string:

type Person<Key = 'name'> = {
  [key in string]?: key extends Key ? string : number
}

function makePerson(vd: Person) {}

makePerson({
  name: 'somename',
  age: 19,
  height: 69,
  weight: 350
})

I tried a method of combining something like type Person<T extends string> = { [key in T]: string } & { [key in Omit<string, T>]: number }
but I couldn't get that to work either .. says that Omit<string, T> is not assignable to string | number | symbol - which makes sense, since T extends string so there'd be no key..

anyways, any help would be greatly appreciated

fallow chasm
#

key in string doesn't really work as a mapped type, it behaves as an index signature

#

i feel like this example is too reduced?

novel bane
#

reduced in what way?

fallow chasm
#

Person might be reduced too far, losing its behavior

#

right now Person is basically string extends Key ? Record<string, string> : Record<string, number>

#

it doesn't really do per-key checking

novel bane
#

exactly..

fallow chasm
#

mapped types work over unions, and the only member of the union is string

#

i'm trying to get the values of the object to all have number as the type
so this is either all of them or none of them? im not quite sure what you're trying to do

#

i might be missing the question

#

OH Person was your attempt, not the existing type.. i misread really badly lol

#

this isn't easily doable, because:

novel bane
#

i'm trying to make it so when i interact with the person i get the correct type for .name vs .someRandomProperty

fallow chasm
#

!:index%

devout voidBOT
#
gerrit0#0
`!gerrit0:index-signatures`:

Index signatures apply to all members of the type that match the signature. Therefore, the following type is invalid, even though TypeScript does not detect it until you actually try to construct a value which conforms to the type.

type Foo = { a: 1 } & { [x: string]: 2 }

TypeScript does not have a way to specify "everything besides defined keys" since it does not have negated types. This pattern cannot be expressed in TypeScript today. A more TS-friendly version of it puts all of the extra properties on a dedicated key:

type Foo = { a: 1; extra: { [x: string]: 2 } }

negated types: https://github.com/microsoft/TypeScript/pull/29317

novel bane
#

ahh

#

the closest i got was by explicitly defining the key named in the union:

type Person = { name?: string } & { [key: string]: number }
function makePerson(person: Person) { return person }
const p = makePerson({})

p.name = 'asdf'
p.asdfsdf = 83

which does give me the right types.. it just doesn't have a dynamic name for the key and unfortunately doesn't really allow me to construct the object with values (and i have to result in the name being defined as possibly undefined

#

but ok, sounds like what i'm trying to do is close to impossible rn.. so i'll just leave it as is

#

thanks for taking your time

fallow chasm
#

if you use exact keys you could achieve it but it's just not doable with disjoint types with an index signature

#
type X<KStrings extends string, KNumbers extends string> = Record<KStrings, string> & <Record<KNumbers, number>