expect(props).toStrictEqual({
foo: ['Symbol', 'String', 'Number'],
- bar: [UNKNOWN_TYPE],
+ bar: ['String', 'Number'],
})
})
})
})
+ test('generic with union type', () => {
+ expect(
+ resolve(`
+ type Wrapped<T> = T | symbol | number
+ defineProps<{foo?: Wrapped<boolean>}>()
+ `).props,
+ ).toStrictEqual({
+ foo: ['Boolean', 'Symbol', 'Number'],
+ })
+ })
+
+ test('generic type /w intersection', () => {
+ expect(
+ resolve(`
+ type Wrapped<T> = T & symbol & number
+ defineProps<{foo?: Wrapped<boolean>}>()
+ `).props,
+ ).toStrictEqual({
+ foo: ['Boolean', 'Symbol', 'Number'],
+ })
+ })
+
test('generic from external-file', () => {
const files = {
'/foo.ts': 'export type P<T> = { foo: T }',
node: Node & MaybeWithScope,
scope: TypeScope = node._ownerScope || ctxToScope(ctx),
isKeyOf = false,
+ typeParameters: Record<string, Node> | undefined = undefined,
): string[] {
try {
switch (node.type) {
case 'TSTypeReference': {
const resolved = resolveTypeReference(ctx, node, scope)
if (resolved) {
- return inferRuntimeType(ctx, resolved, resolved._ownerScope, isKeyOf)
+ if (!node.typeParameters) {
+ return inferRuntimeType(
+ ctx,
+ resolved,
+ resolved._ownerScope,
+ isKeyOf,
+ )
+ } else if (resolved.type === 'TSTypeAliasDeclaration') {
+ const typeParams = Object.create(null)
+ if (resolved.typeParameters) {
+ resolved.typeParameters.params.forEach((p, i) => {
+ typeParams![p.name] = node.typeParameters!.params[i]
+ })
+ }
+ return inferRuntimeType(
+ ctx,
+ resolved.typeAnnotation,
+ resolved._ownerScope,
+ isKeyOf,
+ typeParams,
+ )
+ }
}
-
if (node.typeName.type === 'Identifier') {
+ if (typeParameters && typeParameters[node.typeName.name]) {
+ return inferRuntimeType(
+ ctx,
+ typeParameters[node.typeName.name],
+ scope,
+ isKeyOf,
+ typeParameters,
+ )
+ }
if (isKeyOf) {
switch (node.typeName.name) {
case 'String':
return inferRuntimeType(ctx, node.typeAnnotation, scope)
case 'TSUnionType':
- return flattenTypes(ctx, node.types, scope, isKeyOf)
+ return flattenTypes(ctx, node.types, scope, isKeyOf, typeParameters)
case 'TSIntersectionType': {
- return flattenTypes(ctx, node.types, scope, isKeyOf).filter(
- t => t !== UNKNOWN_TYPE,
- )
+ return flattenTypes(
+ ctx,
+ node.types,
+ scope,
+ isKeyOf,
+ typeParameters,
+ ).filter(t => t !== UNKNOWN_TYPE)
}
case 'TSEnumDeclaration':
types: TSType[],
scope: TypeScope,
isKeyOf: boolean = false,
+ typeParameters: Record<string, Node> | undefined = undefined,
): string[] {
if (types.length === 1) {
- return inferRuntimeType(ctx, types[0], scope, isKeyOf)
+ return inferRuntimeType(ctx, types[0], scope, isKeyOf, typeParameters)
}
return [
...new Set(
([] as string[]).concat(
- ...types.map(t => inferRuntimeType(ctx, t, scope, isKeyOf)),
+ ...types.map(t =>
+ inferRuntimeType(ctx, t, scope, isKeyOf, typeParameters),
+ ),
),
),
]