#Record<string, unknown> accepts a an object type alias, but not an interface

8 messages · Page 1 of 1 (latest)

boreal granite
#

I'm trying to figure out how Record<string, unknown> works. On the whole I don't understand how assignability to Record<string, unknown> works, because it seems to center around whether TS decides to magically add an unwritten index signature or not.

Experimenting with Record<string, unknown>, it seems to accept objects that are defined as type aliases, but not objects that are defined as interfaces. (It also doesn't accept classes, I presume because those are interfaces.) That's unfortunate because it makes it very hard to guess how it will interact with 3p types, let alone my own, since simple aliases and interfaces are otherwise interchangeable in many ways.

More specifically, I'm trying to get a type that means "An object, but not an array or function" (object allows those other types) and was hoping Record<string, unknown> would work. Is there any decent type to use for that?

See playground: https://www.typescriptlang.org/play?ssl=12&ssc=53&pln=1&pc=1#code/JYOwLgpgTgZghgYwgAgGIHt3IN7IB4BcyIArgLYBG0yAvgNwCwAUGAJ4AOKAQnFMgLw58RUpWr1mCADZwAzrOQ8AXjmYBIQsXJU+ggIyMmNZpPQhZYZDExEMWQbk17ahhGYvIKvIj11CnLiZMbuaWoJCwiBA8ACZEAEoQblAxADwWUKAA5gA0yCQgANYg6ADuIAB8AlaYru6WbJwA4phxyInJaRnZeQXFZZXVXlB1ocjScrKxCUnoKelgmSC5+UUl5VWCIBClinBKABQAlHRAA

lavish yokeBOT
#

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

mokeemokee#0

Preview:```ts
interface Foo {
x: number
}
type Bar = {x: number}
class Baz {
x: number = 1
}

const foo: Foo = {x: 1}
const bar: Bar = {x: 1}

const interfaceBad: Record<string, unknown> = foo
const typeGood: Record<string, unknown> = bar
const classBad: Record<string, unknown> = new Baz()```

sleek ridge
#

@boreal granite Yeah, types have an implicit index signature, while interfaces do not. I believe the reason is that interfaces support declaration merging and so are "open", while types cannot be extended later.

#

It isn't anything specific to Record<string, unknown> (except the fact that TS treats [Key in string] mapped types as equivalent to [index: string] index signatures):

lavish yokeBOT
#
retsam19#0

Preview:```ts
type TypeExample = {x: number; y: number}
interface InterfaceExample {
x: number
y: number
}

declare const x: TypeExample
declare const y: InterfaceExample

interface IndexSignature {
[key: string]: number
}

let xx: IndexSignature = x
let yy: IndexSignature = y```

sleek ridge
#

More specifically, I'm trying to get a type that means "An object, but not an array or function" (object allows those other types) and was hoping Record<string, unknown> would work. Is there any decent type to use for that?
This wouldn't work, anyway. Arrays and functions are both objects in JS, I'm pretty sure it's fairly difficult to make a "Plain-old-JS-Object" type that doesn't accept other kinds of objects.

boreal granite
#

I definitely know that "Plain old JS Object" can't be done, but Record<string, unknown> does work to prevent arrays and other things, which I was surprised to find. So I almost thought it was what I wanted. But now it seems that was just a side effect of those things happening to be interfaces, instead of it actually encoding anything about object-ness.

sleek ridge
#

Yeah, I think that's accurate