简体   繁体   中英

Recursive type properties in Typescript

I try to declare a recursive type with following interface

interface Map<T> {
  [key: string]: Map<T> | T;
}

However, when I try to get property of this object:

const map: Map<number> = {
  a: {
    b: {
      c: 2
    }
  }
};

console.log(map.a.b.c);

I get an error:

TS2339:Property 'b' does not exist on type 'number | Map<number>'.  Property 'b' does not exist on type 'number'.

I understand why it happens, but is there a workaround?

PS My tsconfig.json is following:

{
  "compilerOptions": {
    "declaration": true,
    "downlevelIteration": true,
    "importHelpers": true,
    "lib": [
      "dom",
      "es2017"
    ],
    "module": "es2015",
    "moduleResolution": "node",
    "noFallthroughCasesInSwitch": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "sourceMap": true,
    "strict": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ]
  }
}

An indexable type isn't aware of what keys it has, so you cannot use the dot notation, instead, you'll need to use:

console.log(map["a"]["b"]["c"]);

Notice however that the type for map["a"]["b"] is any , you'll need to use (map["a"] as MyMap<number>)["b"] to get the right type.

You shouldn't be using the name Map for your interface because there's now a built-in type called Map ( type definition ).

If the example you posted really shows your use case then I suggest that you don't annotate the map variable at all:

const map = {
    a: {
        b: {
            c: 2
        }
    }
};
console.log(map.a.b.c); // this is fine now

The compiler is smart enough to infer the type of map to:

type map = {
    a: {
        b: {
            c: number;
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM