From c5a024490b0ba2397521078fa1ce3ec5b0fcbcfa Mon Sep 17 00:00:00 2001 From: KazariEX Date: Sat, 18 Oct 2025 20:59:41 +0800 Subject: [PATCH 1/3] fix(compiler-sfc): resolve template literals without expressions as string literals --- .../__tests__/compileScript/resolveType.spec.ts | 4 +++- packages/compiler-sfc/src/script/resolveType.ts | 6 +----- packages/compiler-sfc/src/script/utils.ts | 4 +++- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts index eff2f01b981..9b9d85eb5b3 100644 --- a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts @@ -20,6 +20,7 @@ describe('resolveType', () => { foo: number // property bar(): void // method 'baz': string // string literal key + [\`qux\`]: boolean // template literal key (e: 'foo'): void // call signature (e: 'bar'): void }>()`) @@ -27,6 +28,7 @@ describe('resolveType', () => { foo: ['Number'], bar: ['Function'], baz: ['String'], + qux: ['Boolean'], }) expect(calls?.length).toBe(2) }) @@ -195,7 +197,7 @@ describe('resolveType', () => { type T = 'foo' | 'bar' type S = 'x' | 'y' defineProps<{ - [\`_\${T}_\${S}_\`]: string + [K in \`_\${T}_\${S}_\`]: string }>() `).props, ).toStrictEqual({ diff --git a/packages/compiler-sfc/src/script/resolveType.ts b/packages/compiler-sfc/src/script/resolveType.ts index b40c7cd7cbf..85d9bbc8c82 100644 --- a/packages/compiler-sfc/src/script/resolveType.ts +++ b/packages/compiler-sfc/src/script/resolveType.ts @@ -337,12 +337,8 @@ function typeElementsToMap( } ;(e as MaybeWithScope)._ownerScope = scope const name = getId(e.key) - if (name && !e.computed) { + if (name !== null) { res.props[name] = e as ResolvedElements['props'][string] - } else if (e.key.type === 'TemplateLiteral') { - for (const key of resolveTemplateKeys(ctx, e.key, scope)) { - res.props[key] = e as ResolvedElements['props'][string] - } } else { ctx.error( `Unsupported computed key in type referenced by a macro`, diff --git a/packages/compiler-sfc/src/script/utils.ts b/packages/compiler-sfc/src/script/utils.ts index 1b7da0e3497..7c10cf8b15a 100644 --- a/packages/compiler-sfc/src/script/utils.ts +++ b/packages/compiler-sfc/src/script/utils.ts @@ -76,7 +76,9 @@ export function getId(node: Expression) { ? node.name : node.type === 'StringLiteral' ? node.value - : null + : node.type === 'TemplateLiteral' && !node.expressions.length + ? node.quasis.map(q => q.value.cooked).join('') + : null } const identity = (str: string) => str From 604cfdf49989a78613153ab9e2bc7eb5d74ee780 Mon Sep 17 00:00:00 2001 From: KazariEX Date: Sat, 18 Oct 2025 21:15:13 +0800 Subject: [PATCH 2/3] fix: `computed` condition --- packages/compiler-sfc/src/script/resolveType.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compiler-sfc/src/script/resolveType.ts b/packages/compiler-sfc/src/script/resolveType.ts index 85d9bbc8c82..ab4d54d4909 100644 --- a/packages/compiler-sfc/src/script/resolveType.ts +++ b/packages/compiler-sfc/src/script/resolveType.ts @@ -337,7 +337,7 @@ function typeElementsToMap( } ;(e as MaybeWithScope)._ownerScope = scope const name = getId(e.key) - if (name !== null) { + if (name !== null && (!e.computed || e.key.type === 'TemplateLiteral')) { res.props[name] = e as ResolvedElements['props'][string] } else { ctx.error( From 5ce53c0802691f6c6040c3e0b7a8236cbd3489e7 Mon Sep 17 00:00:00 2001 From: KazariEX Date: Sat, 18 Oct 2025 21:24:29 +0800 Subject: [PATCH 3/3] refactor: extract `getStringLiteralKey` --- .../compileScript/resolveType.spec.ts | 2 ++ .../compiler-sfc/src/script/resolveType.ts | 8 +++---- packages/compiler-sfc/src/script/utils.ts | 22 ++++++++++++++++--- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts index 9b9d85eb5b3..557f6ec5d93 100644 --- a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts @@ -21,6 +21,7 @@ describe('resolveType', () => { bar(): void // method 'baz': string // string literal key [\`qux\`]: boolean // template literal key + 123: symbol // numeric literal key (e: 'foo'): void // call signature (e: 'bar'): void }>()`) @@ -29,6 +30,7 @@ describe('resolveType', () => { bar: ['Function'], baz: ['String'], qux: ['Boolean'], + 123: ['Symbol'], }) expect(calls?.length).toBe(2) }) diff --git a/packages/compiler-sfc/src/script/resolveType.ts b/packages/compiler-sfc/src/script/resolveType.ts index ab4d54d4909..9cb4ce8e7c2 100644 --- a/packages/compiler-sfc/src/script/resolveType.ts +++ b/packages/compiler-sfc/src/script/resolveType.ts @@ -29,6 +29,7 @@ import { createGetCanonicalFileName, getId, getImportedName, + getStringLiteralKey, joinPaths, normalizePath, } from './utils' @@ -336,8 +337,8 @@ function typeElementsToMap( Object.assign(scope.types, typeParameters) } ;(e as MaybeWithScope)._ownerScope = scope - const name = getId(e.key) - if (name !== null && (!e.computed || e.key.type === 'TemplateLiteral')) { + const name = getStringLiteralKey(e) + if (name !== null) { res.props[name] = e as ResolvedElements['props'][string] } else { ctx.error( @@ -2025,8 +2026,7 @@ function findStaticPropertyType(node: TSTypeLiteral, key: string) { const prop = node.members.find( m => m.type === 'TSPropertySignature' && - !m.computed && - getId(m.key) === key && + getStringLiteralKey(m) === key && m.typeAnnotation, ) return prop && prop.typeAnnotation!.typeAnnotation diff --git a/packages/compiler-sfc/src/script/utils.ts b/packages/compiler-sfc/src/script/utils.ts index 7c10cf8b15a..bda64dc2e41 100644 --- a/packages/compiler-sfc/src/script/utils.ts +++ b/packages/compiler-sfc/src/script/utils.ts @@ -7,6 +7,8 @@ import type { ImportSpecifier, Node, StringLiteral, + TSMethodSignature, + TSPropertySignature, } from '@babel/types' import path from 'path' @@ -76,9 +78,23 @@ export function getId(node: Expression) { ? node.name : node.type === 'StringLiteral' ? node.value - : node.type === 'TemplateLiteral' && !node.expressions.length - ? node.quasis.map(q => q.value.cooked).join('') - : null + : null +} + +export function getStringLiteralKey( + node: TSPropertySignature | TSMethodSignature, +): string | null { + return node.computed + ? node.key.type === 'TemplateLiteral' && !node.key.expressions.length + ? node.key.quasis.map(q => q.value.cooked).join('') + : null + : node.key.type === 'Identifier' + ? node.key.name + : node.key.type === 'StringLiteral' + ? node.key.value + : node.key.type === 'NumericLiteral' + ? String(node.key.value) + : null } const identity = (str: string) => str