]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-sfc): support string indexed type in macros
authorEvan You <yyx990803@gmail.com>
Thu, 13 Apr 2023 03:21:09 +0000 (11:21 +0800)
committerEvan You <yyx990803@gmail.com>
Thu, 13 Apr 2023 03:21:09 +0000 (11:21 +0800)
packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts
packages/compiler-sfc/src/script/resolveType.ts

index 5d5a424f26bcaea479cf84b96793776735343b83..1485cfe83aee7e9a8d6c0a4480db04feae953f9d 100644 (file)
@@ -215,6 +215,30 @@ describe('resolveType', () => {
     })
   })
 
+  test('indexed access type', () => {
+    expect(
+      resolve(`
+    type T = { bar: number }
+    type S = { nested: { foo: T['bar'] }}
+    type Target = S['nested']
+    `).props
+    ).toStrictEqual({
+      foo: ['Number']
+    })
+  })
+
+  // test('namespace', () => {
+  //   expect(
+  //     resolve(`
+  //   type T = { foo: number, bar: string, baz: boolean }
+  //   type K = 'foo' | 'bar'
+  //   type Target = Omit<T, K>
+  //   `).props
+  //   ).toStrictEqual({
+  //     baz: ['Boolean']
+  //   })
+  // })
+
   describe('errors', () => {
     test('error on computed keys', () => {
       expect(() => resolve(`type Target = { [Foo]: string }`)).toThrow(
index ecd3838be7b5bc91f57a8d42cc2f97f2ca13d9e4..0b6969ad8f04c67f175d77d6be664915afaaa04c 100644 (file)
@@ -1,4 +1,5 @@
 import {
+  Identifier,
   Node,
   Statement,
   TSCallSignatureDeclaration,
@@ -8,6 +9,7 @@ import {
   TSMappedType,
   TSMethodSignature,
   TSPropertySignature,
+  TSQualifiedName,
   TSType,
   TSTypeAnnotation,
   TSTypeElement,
@@ -62,6 +64,34 @@ function innerResolveTypeElements(
     case 'TSFunctionType': {
       return { props: {}, calls: [node] }
     }
+    case 'TSUnionType':
+    case 'TSIntersectionType':
+      return mergeElements(
+        node.types.map(t => resolveTypeElements(ctx, t)),
+        node.type
+      )
+    case 'TSMappedType':
+      return resolveMappedType(ctx, node)
+    case 'TSIndexedAccessType': {
+      if (
+        node.indexType.type === 'TSLiteralType' &&
+        node.indexType.literal.type === 'StringLiteral'
+      ) {
+        const resolved = resolveTypeElements(ctx, node.objectType)
+        const key = node.indexType.literal.value
+        const targetType = resolved.props[key].typeAnnotation
+        if (targetType) {
+          return resolveTypeElements(ctx, targetType.typeAnnotation)
+        } else {
+          break
+        }
+      } else {
+        ctx.error(
+          `Unsupported index type: ${node.indexType.type}`,
+          node.indexType
+        )
+      }
+    }
     case 'TSExpressionWithTypeArguments': // referenced by interface extends
     case 'TSTypeReference': {
       const resolved = resolveTypeReference(ctx, node)
@@ -82,16 +112,8 @@ function innerResolveTypeElements(
         )
       }
     }
-    case 'TSUnionType':
-    case 'TSIntersectionType':
-      return mergeElements(
-        node.types.map(t => resolveTypeElements(ctx, t)),
-        node.type
-      )
-    case 'TSMappedType':
-      return resolveMappedType(ctx, node)
   }
-  ctx.error(`Unsupported type in SFC macro: ${node.type}`, node)
+  ctx.error(`Unresolvable type in SFC macro: ${node.type}`, node)
 }
 
 function typeElementsToMap(
@@ -342,8 +364,15 @@ function getReferenceName(
   if (ref.type === 'Identifier') {
     return ref.name
   } else {
-    // TODO qualified name, e.g. Foo.Bar
-    return []
+    return qualifiedNameToPath(ref)
+  }
+}
+
+function qualifiedNameToPath(node: Identifier | TSQualifiedName): string[] {
+  if (node.type === 'Identifier') {
+    return [node.name]
+  } else {
+    return [...qualifiedNameToPath(node.left), node.right.name]
   }
 }
 
@@ -376,8 +405,11 @@ function recordType(node: Node, types: Record<string, Node>) {
   switch (node.type) {
     case 'TSInterfaceDeclaration':
     case 'TSEnumDeclaration':
-      types[node.id.name] = node
+    case 'TSModuleDeclaration': {
+      const id = node.id.type === 'Identifier' ? node.id.name : node.id.value
+      types[id] = node
       break
+    }
     case 'TSTypeAliasDeclaration':
       types[node.id.name] = node.typeAnnotation
       break
@@ -542,6 +574,17 @@ export function inferRuntimeType(
     case 'TSSymbolKeyword':
       return ['Symbol']
 
+    case 'TSIndexedAccessType': {
+      if (
+        node.indexType.type === 'TSLiteralType' &&
+        node.indexType.literal.type === 'StringLiteral'
+      ) {
+        const resolved = resolveTypeElements(ctx, node.objectType)
+        const key = node.indexType.literal.value
+        return inferRuntimeType(ctx, resolved.props[key])
+      }
+    }
+
     default:
       return [UNKNOWN_TYPE] // no runtime check
   }