1

I was wondering if it is possible to check for undefined values in object with typescript using helper function?

type Page = {
  hero?: {
    title?: string
  },
  about: {
    title?: string,
  }
}

const obj: Page = {
  hero: undefined,
  about: {
    title: 'hello',
  }
}

const check = <T extends Object,>(obj: T, values: Array<keyof T>) => {
  values.forEach((value)=>{
    if (!obj[value]){
      throw new Error(`${value} is missing!`)
    }
  })
};

check(obj, ['hero'])

obj.hero.title

but it still says that " Object is possibly 'undefined' " . Is there any way I can achieve something like that with typescript?

EDIT: I am aware that I can explicitly check for that property using ? . I wonder if it is possible to make ts happy using helper function as described above.

3
  • Note that !obj[value] will throw for any falsey value, not just undefined. If you want to know if it's undefined, you have to compare to undefined. Commented Apr 13, 2022 at 11:27
  • since the check will ensure obj.hero is not undefined, you can tell the transpiler so like obj.hero!.title Commented Apr 13, 2022 at 11:32
  • The compiler doesn't know that check() is doing an assertion on obj. You could explicitly annotate check as an assertion function if you want this behavior. It looks like this. Does that meet your needs? If so I could write up an answer; if not, what am I missing? Commented Apr 13, 2022 at 13:04

3 Answers 3

2

You can make helper function like this. It is a bit hacky but I think it kind of does what you want.


type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }

const check = <T extends Object, V extends Array<keyof T>>(obj: T, values: V): WithRequired<T, V[number]> => {
  values.forEach((value)=>{
    if (!obj[value]){
      throw new Error(`${value} is missing!`)
    }
  })

  return obj as WithRequired<T, V[number]>;
};

const objChecked = check(obj, ['hero'])

objChecked.hero.title

Playground

Sign up to request clarification or add additional context in comments.

1 Comment

Since this answer has already been accepted, you might look into editing to talk about assertion functions which is what the question is apparently asking to do. Your version is what we'd have to do before assertion functions were introduced; now we can do something like this.
0

Either use ? or if condition.

In JSX, use ternary.

{obj?.hero ? {use of hero} : null}

OR

In Functions, use if.

if (obj && obj.hero) {Your code here for hero}

5 Comments

I downvoted because: 1. Misusing a ternary conditional operator, 2. obj.hero? isn't valid code 3. why no optional chaining in the second snippet? 4. This doesn't answer the question.
@jabaa For 1st, there is no misuse, anyone can use ternary for conditonal checks, Updated the code for 2nd. For 3rd, if you will have obj then it will check for obj.hero and if you also have obj.hero then it will go inside the condition.
Using a ternary conditional operator for conditional code execution without assignment and with null as last operand is considered misuse and bad code style. You won't pass any serious code review. Why do you not use optional chaining in the second snippet? How does it answer, whether it's possible to implement the helper function?
Anh, I got that, Ternary can be used in JSX snippet, updating with the same.
Why do you answer this question for JSX. It's neither tagged with React nor JSX and the code in question doesn't contain any JSX. And even with JSX I wouldn't consider this a good. Solution. You're converting undefined` and false to null. Why?
0

Your problem is that you're confusing types with values.

When you write:

check(obj, ['hero'])
obj.hero

You are thinking something like "ok, so we know obj.hero can't be undefined", because you checked the value.

But the compiler can't see runtime values, it only works with types, and the type of the argument to check is Array<keyof T> it doesn't (and can't!) know that you checked that specific key that you're referencing on the next line.

Same deal with the thrown error, the compiler has limited ability to prove things about runtime side effects like thrown errors. It can do basic control flow analysis to realize that e.g. code after a throw is unreachable, but don't expect too much of it.

There isn't really any need for a helper function for this anyway:

if (obj?.hero !== undefined) {
  console.log(obj.hero.title); // works!
}

will suffice.

Playground

Note that per my comment on your question, you really want to check for undefined if that's what you're worried about: with your original code the following (non-exhaustive) list of values will throw an error: null, undefined, 0, NaN, '', false and so on.

3 Comments

@SergeyGridin you are correct. Working on a fix, but seriously just use the inline check.
But it looks like you can't wrap this check in a function. At least I can't .
@jabaa not without a cast, not that I can figure out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.