1

I have an arbitrary structure consisting of nested arrays and objects, with ValidationError objects as leaves. To type this I need a recursive type as shown in Typescript guidelines.

While the assignment (const x = ...) seems to pass the type check, accessing the structure (x.errors.a) gives a TypeScript error which I cannot understand:

Error: TS2339: Property 'a' does not exist on type 'ValidationResultElement'.

Property 'a' does not exist on type 'ValidationResultObject'.

see code on TypeScript Playground

export interface ValidationResult {
  errors: ValidationResultElement;
}

type ValidationResultElement =
  ValidationResultObject | ValidationResultArray | ValidationError;

interface ValidationResultArray extends Array<ValidationResultElement> {
}

interface ValidationResultObject {
  [key: string]: ValidationResultElement;
}

interface ValidationError {
  details: string;
}

// This works:
const x: ValidationResult = {
    errors: { a: { b: [{ c: { details: 'foo' } }] } }
};

// This produces a type error:
console.log(x.errors.a);
1
  • 3
    The problem is you may have to tell TypeScript which of the three ResultElements in the union to consider (x.errors as ValidationResultObject).a Commented Dec 4, 2018 at 10:08

1 Answer 1

1

You need to narrow the type. This is an example but I have caveats!

function isValidationResultObject(obj: any): obj is ValidationResultObject {
  return (obj && (!obj.type) && (!obj.length));
}

if (isValidationResultObject(x.errors)) {
  console.log(x.errors.a);
}

I have chucked together a custom type guard to eliminate the other types, but it is probably wrong, it just demonstrates the concept. You need to write a type guard that works.

You may find a discriminated union type makes this easier that delving into lots of properties.

You could force the type to narrow with an assertion, but a type guard is more honest and ensures you are really dealing with the type you expect.

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

Comments

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.