Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript test if value instance of Record

Tags:

typescript

Is there a way to test if a value is an instance of a Record type in TypeScript? I want a case that properly distinguishes when the variable could potentially also be an array.

function(a: Record<string,string>|string[]) {
  if( a instanceof Record ) {
     do_stuff( a.key )
  }
}

I know this is problematic in JavaScript because both of my types are objects, thus typeof(a) == "object" in both cases. I'm hoping there's perhaps a special TypeScript way to accomplish this.

I understand the Record type doesn't really exist at runtime, but the example should clarify what I'm trying to do. It's also important that the test properly informs TypeScript of the narrowed type, so that a.key doesn't produce an error.

like image 852
edA-qa mort-ora-y Avatar asked Feb 14 '26 03:02

edA-qa mort-ora-y


1 Answers

As per the documentation, a Record<K, V> is an object whose keys are K and values are V. A "true" Record<string, string> doesn't exist because of the prototype chain, which causes objects to inherit e.g. a .toString() method. But taking into consideration the complete set of inherited properties would be cumbersome (related discussion on the TS GitHub), so it's clearest to think of the Record type working with an object's own properties:

// Allowed:
const foo: Record<string, string> = { bar: "bar" }

// Even though functions aren't a subtype of string:
type inheritedToString = typeof foo.toString
//   ~~~~~~~~~~~~~~~~~
//   inheritedToString = () => string

This means you'll most likely get far enough by combining one or many of the ways of iterating over properties in an object with TypeScript's type predicates:

function isStringRecord(obj: unknown): obj is Record<string, string> {
  if (typeof obj !== "object")
    return false

  if (Array.isArray(obj))
    return false

  if (Object.getOwnPropertySymbols(obj).length > 0)
    return false
  
  return Object.getOwnPropertyNames(obj)
    .every(prop => typeof obj[prop] === "string")
}

Now you'll have:

const foo = { bar: "bar" } as unknown

if (isStringRecord(foo)) {
  foo // foo: Record<string, string>
}
like image 131
Emma Avatar answered Feb 15 '26 17:02

Emma



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!