0

I know and understand what unknown in TypeScript does. I have an untrusted input, typed as unknown. I would like to check if it has a truthy value under input.things.0.

function isFullOfGreatThings(input: unknown) {
  return Boolean(input?.things?.[0]);
}

TypeScript complains that Object is of type 'unknown' - however I am aware that the type is unknown, which is why I am testing for the value beneath that key.

How can I test for keys in an unknown object in TypeScript?

Dharman
  • 30,962
  • 25
  • 85
  • 135
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
  • Have you read e.g. https://stackoverflow.com/q/69705488/3001761, https://stackoverflow.com/q/51624117/3001761? – jonrsharpe Jun 22 '22 at 16:59
  • 1
    Yes the second example works @jonrsharpe. I'll mark this as a duplicate. – mikemaccana Jun 22 '22 at 17:36
  • Answers do not belong in the question body. If you have an alternative solution please add it on the question linked as a duplicate. That's where your solution belongs – Dharman Jun 23 '22 at 09:33
  • Sure Dharman. The only reason I reverted the change was because @jonrsharpe modified the question with no comment or reason why. – mikemaccana Jun 28 '22 at 09:38
  • @jonrsharpe your comment seems somewhat snarky - the reason I added some text to the question was to help others in the specific situation I was in, without seeking karma by adding a separate answer. Next time you remove something that helpful to others, it would be useful to add a comment like "I've rolled this back to a previous version - you addition was helpful but it belongs as a separate answer". Also, as you know, I'm limited to tagging one person and I'd rather address you than the other user as the other user doesn't need to change their behavior. – mikemaccana Jun 28 '22 at 10:06

1 Answers1

1

It depends on how safe you want to be and how much runtime code you want as part of your type-checking safety procedure.

If all you really care about is a boolean result, then you can just cast input as any and use what you have, wrapped in a try...catch that returns false in the catch block:

TS Playground

function isFullOfGreatThings (input: unknown): boolean {
  try {
    return Boolean((input as any)?.things?.[0]);
  }
  catch {
    return false;
  }
}

Compiled JS from the TS Playground:

"use strict";
function isFullOfGreatThings(input) {
    try {
        return Boolean(input?.things?.[0]);
    }
    catch {
        return false;
    }
}
const inputs = [
    { get things() { throw new Error('Oops'); } },
    { things: { get 0() { throw new Error(`It's a trap`); } } },
    'hello',
    2,
    false,
    true,
    null,
    ['a', 'b', 'c'],
    { a: 1 },
    { things: ['something special'] },
    { things: 'text' },
    { things: { nested: 'things' } },
    Symbol('snowflake'),
    BigInt(0),
];
for (const input of inputs) {
    console.log(input, isFullOfGreatThings(input));
}
jsejcksn
  • 27,667
  • 4
  • 38
  • 62
  • I've been avoiding `any` as it's often stated that `unknown` is the preferred mechanism to handle data from uncontrolled sources. Why is the catch necessary? – mikemaccana Jun 22 '22 at 17:32
  • @mikemaccana "With great power comes great responsibility": When you cast to `any`, you are telling the compiler that you're "handling things from here" (it's very much like disabling the compiler's type-checking for that value), so you're fully responsible for managing safe interactions with the data. There's nothing wrong with `any` when it's necessary used **responsibly** (and this is one of those cases). "_Why is the catch necessary?_": Look at the test inputs in the compiled JS snippet. – jsejcksn Jun 22 '22 at 17:37
  • @jsejckson Why would there be an error accessing the property when `?.` is used? – mikemaccana Jun 22 '22 at 17:39
  • @mikemaccana In the case that the property is a getter that throws an exception. – jsejcksn Jun 22 '22 at 17:39
  • Ah fair enough @jsejcksn. The user data here is in the form of JSON so getters / setters are not included. – mikemaccana Jun 22 '22 at 17:42