Reputation: 1124
I've got two type definitions describing almost the same thing:
// compilation fails with `Type alias 'StringOrRecordOfStrings' circularly references itself.ts(2456)`
type StringOrRecordOfStrings = string | string[] | Record<string, StringOrRecordOfStrings>;
// compiles without problems
type StringOrRecordOfStrings = string | string[] | { [property: string]: StringOrRecordOfStrings };
Is anyone able to explain why the first type definition doesn't compile?
[property: string | number | symbol]
.Upvotes: 2
Views: 751
Reputation: 15096
The reason why Record<string, StringOrRecordOfStrings>
is not allowed is that Record
is a generic type, rather than a class or an interface.
There isn't a lot of clear documentation on this, but recursive type references for properties in objects, index signatures, and mapped types have been around for quite a while. The following works as early as TypeScript 3.3:
type Recursive = {p: Recursive} | {[p: string]: Recursive} | {[Key in 'a']: Recursive}
This is why your second example type (with an index signature) checks.
TypeScript 3.7 extended the support for recursive references, as explained in this PR:
Array<Foo>
).Foo[]
).[string, Foo?]
).So now, also these three examples are valid:
type RecursiveCI = Promise<RecursiveCI>
type RecursiveT = [number, boolean, RecursiveT]
type RecursiveA = RecursiveA[]
I assume the sample is just test code, but you can get it to type check by using a helper interface like this:
type StringOrRecordOfStrings = string | string[] | Record<string, RecordInterface>
interface RecordInterface extends Record<string, StringOrRecordOfStrings> {}
Upvotes: 2