2

I struggle to understand why typescript is not happy with the typing of my json:

const myjson = { 
    "cube": {
        "points":
            [[-100, -100, -100],
             [ 100, -100, -100],
            ],
        "triangles":
            [[0,1,2,"rgba(255,255,127,0.3)"],
             [2,3,0,"rgba(255,255,127,0.3)"]
            ]
    },
    "pyramid": {
        "points":
            [[   0, -100,    0],
             [ 100,  100, -100],
            ],
        "triangles":
            [[0,1,2,"rgba(255,255,127,0.3)"],
             [0,2,3,"rgba(0,255,127,0.3)"],
            ]
    }
}

interface Data3D {
    [k: string]: {
    points: number[][];
    triangles: [number, number, number, string][];
  }
}

const make3D = (jsondata: Data3D) => {
    console.log(jsondata);
}

make3D(myjson);

Typescript complains about the triangles type:

Types of property 'triangles' are incompatible.
        Type '(string | number)[][]' is not assignable to type '[number, number, number, string][]'.
          Type '(string | number)[]' is not assignable to type '[number, number, number, string]'.
            Target requires 4 element(s) but source may have fewer.(2345)

I type using a custom type [number, number, number string] but Typescript reports it could have fewer elements ?

if I change the type of triangles to :

triangles: (number | string)[][];

it works.

why can't I type it more precisely like that ? :

triangles: [number, number, number, string][];
2
  • 1
    JSON is a text representation of some data structure. There isn't any JSON in your question, just a JavaScript object and some variables whose names are misleading. Commented Oct 30, 2021 at 12:21
  • @axiac whether it's a JSON or not is not relevant, my question is about the typings. I simplified the source code, but in my real code I'm importing json from a file, and the content of the file is what is here named as myjson. Commented Oct 30, 2021 at 12:34

1 Answer 1

1

When you say,

const myjson = { 
    "cube": {
        "points":
            [[-100, -100, -100],
             [ 100, -100, -100],
            ],
        "triangles":
            [[0,1,2,"rgba(255,255,127,0.3)"],
             [2,3,0,"rgba(255,255,127,0.3)"]
            ]
    },
    "pyramid": {
        "points":
            [[   0, -100,    0],
             [ 100,  100, -100],
            ],
        "triangles":
            [[0,1,2,"rgba(255,255,127,0.3)"],
             [0,2,3,"rgba(0,255,127,0.3)"],
            ]
    }
};

const cubeTriangle = myjson.cube.triangles[0];
// infers as 
// const cubeTriangle: (string | number)[]

myjson.cube.triangles[number] gets inferred as (string | number)[], which is the most logical inference for a random JSON. Typescript does not know that it is Data3D it is looking at. So, you need to explicitly tell TS that the JSON is in fact Data3D

const myjson: Data3D = { 
    cube: {
        points:
            [[-100, -100, -100],
             [ 100, -100, -100],
            ],
        triangles:
            [[0,1,2,"rgba(255,255,127,0.3)"] ,
             [2,3,0,"rgba(255,255,127,0.3)"],
            ]
    },
    pyramid: {
        points:
            [[   0, -100,    0],
             [ 100,  100, -100],
            ],
        triangles:
            [[0,1,2,"rgba(255,255,127,0.3)"],
             [0,2,3,"rgba(0,255,127,0.3)"],
            ]
    }
};

const cubeTriangle = myjson.cube.triangles[0];
// const cubeTriangle: TriangleTuple
// if you don't create an TriangleTuple type
// it would say 
// const cubeTriangle: [number, number, number, string]
// I just like to keep readability high

type TriangleTuple = [number, number, number, string];

TS Playground: https://tsplay.dev/WGk3JW

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

2 Comments

I was adding like you explain the tuple [number, number, number, string], so the problem was indeed that my object was no typed with Data3D. Thank you.
@aestheticsData -- Please consider marking this answer correct and upvote it if you liked the effort. Here is the stackoverflow etiquette on accepting answers.

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.