Jelajahi Sumber

fix(compiler-sfc): support as keyword with template literal types (#11100)

close #10962
Zhaolin Liang 1 tahun lalu
induk
melakukan
2594b1df57

+ 31 - 0
packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts

@@ -1198,6 +1198,37 @@ describe('resolveType', () => {
       })
       })
     })
     })
   })
   })
+
+  describe('template literals', () => {
+    test('mapped types with string type', () => {
+      expect(
+        resolve(`
+      type X = 'a' | 'b'
+      defineProps<{[K in X as \`\${K}_foo\`]: string}>()
+      `).props,
+      ).toStrictEqual({
+        a_foo: ['String'],
+        b_foo: ['String'],
+      })
+    })
+
+    // #10962
+    test('mapped types with generic parameters', () => {
+      const { props } = resolve(`
+      type Breakpoints = 'sm' | 'md' | 'lg'
+      type BreakpointFactory<T extends string, V> = {
+        [K in Breakpoints as \`\${T}\${Capitalize<K>}\`]: V
+      }
+      type ColsBreakpoints = BreakpointFactory<'cols', number>
+      defineProps<ColsBreakpoints>()
+      `)
+      expect(props).toStrictEqual({
+        colsSm: ['Number'],
+        colsMd: ['Number'],
+        colsLg: ['Number'],
+      })
+    })
+  })
 })
 })
 
 
 function resolve(
 function resolve(

+ 11 - 2
packages/compiler-sfc/src/script/resolveType.ts

@@ -188,7 +188,7 @@ function innerResolveTypeElements(
         node.type,
         node.type,
       )
       )
     case 'TSMappedType':
     case 'TSMappedType':
-      return resolveMappedType(ctx, node, scope)
+      return resolveMappedType(ctx, node, scope, typeParameters)
     case 'TSIndexedAccessType': {
     case 'TSIndexedAccessType': {
       const types = resolveIndexType(ctx, node, scope)
       const types = resolveIndexType(ctx, node, scope)
       return mergeElements(
       return mergeElements(
@@ -450,9 +450,18 @@ function resolveMappedType(
   ctx: TypeResolveContext,
   ctx: TypeResolveContext,
   node: TSMappedType,
   node: TSMappedType,
   scope: TypeScope,
   scope: TypeScope,
+  typeParameters?: Record<string, Node>,
 ): ResolvedElements {
 ): ResolvedElements {
   const res: ResolvedElements = { props: {} }
   const res: ResolvedElements = { props: {} }
-  const keys = resolveStringType(ctx, node.typeParameter.constraint!, scope)
+  let keys: string[]
+  if (node.nameType) {
+    const { name, constraint } = node.typeParameter
+    scope = createChildScope(scope)
+    Object.assign(scope.types, { ...typeParameters, [name]: constraint })
+    keys = resolveStringType(ctx, node.nameType, scope)
+  } else {
+    keys = resolveStringType(ctx, node.typeParameter.constraint!, scope)
+  }
   for (const key of keys) {
   for (const key of keys) {
     res.props[key] = createProperty(
     res.props[key] = createProperty(
       {
       {