0

I'm trying to achieve an object type with a fixed property whose value is either an array or a number.

type Gt = {$gt: Array<any> | number}

export default (expression: Array<any> | number): Gt => {
  if (Array.isArray(expression)) {
    let gt: Gt = {$gt: []}

    const [key, value] = expression
    gt.$gt.push(key, value)

    return gt
  } else {
    return {$gt: expression}
  }
}

I get the following error:

src/operators/gt.ts (15,12): Property 'push' does not exist on type 'number | any[]'.

When I change it to type Gt = {$gt: any} it works fine. I don't understand why.

5
  • 1
    Since $gt can be a number and numbers do not have push yo7 get that error. Commented May 11, 2018 at 18:32
  • That's why I do the check: if (Array.isArray(expression)). Isn't that enough for TypeScript? What else could I do? Commented May 11, 2018 at 18:32
  • 1
    The isArray check is being done on expression not on gt.$gt, which is the thing that you are calling push on. Commented May 11, 2018 at 18:41
  • Btw you can cast an element by adding <type> in front of it. Where type of course is the type of you want to cast to Commented May 11, 2018 at 18:52
  • Thank you for this. But this isn't applicable in this case, is it? Commented May 11, 2018 at 18:53

2 Answers 2

2

You are overriding TypeScript's understanding of the gt object you are creating when you explicitly set its type to Gt.

Try changing the line

let gt: Gt = {$gt: []}

to

let gt = {$gt: []}

That explicit typing overrides TypeScript knowing that $gt in this case is an array and instead tells it that it's Array<any> | number even though you've explicitly made it an array.

Doing this change will not conflict with the return type of the function, since the resulting object still conforms to the Gt type.

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

Comments

1

Your problem is in the line

let gt: Gt = {$gt: []}

Yes, you know that gt.$gt contains an array, but, by casting {$gt: []} to Gt, the TypeScript compiler loses that information, so, when you later do gt.$gt, it only knows that gt is of type Gt, and so $gt can contain either an array or a number. This might be stupid, but the compiler can't keep track, along with type information, the previous types of a value.

That's why you can't do

const p : string = [];

but if works if you write

const p: string = [] as any;

because [] can be assigned to any, and any can be assigned to anything.

You don't need to declare gt as being Gt, the type {$gt: Array<any>} can be assigned to Gt, so you should write the function this way:

(expression: Array<any> | number): Gt => {
  if (Array.isArray(expression)) {
    let gt = {$gt: []}

    const [key, value] = expression
    gt.$gt.push(key, value)

    return gt
  } else {
    return {$gt: expression}
  }
}

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.