diff --git a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts index 2f5b23309..6f07cfcfa 100644 --- a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts @@ -397,6 +397,11 @@ describe('resolveType', () => { resolve(`import { X } from './foo'; defineProps()`) ).toThrow(`Failed to resolve import source "./foo" for type X`) }) + + test('should not error on unresolved type when inferring runtime type', () => { + expect(() => resolve(`defineProps<{ foo: T }>()`)).not.toThrow() + expect(() => resolve(`defineProps<{ foo: T['bar'] }>()`)).not.toThrow() + }) }) }) diff --git a/packages/compiler-sfc/src/parse.ts b/packages/compiler-sfc/src/parse.ts index b46c5ea13..29c91cc19 100644 --- a/packages/compiler-sfc/src/parse.ts +++ b/packages/compiler-sfc/src/parse.ts @@ -47,6 +47,13 @@ export interface SFCScriptBlock extends SFCBlock { imports?: Record scriptAst?: import('@babel/types').Statement[] scriptSetupAst?: import('@babel/types').Statement[] + warnings?: string[] + /** + * Fully resolved dependency file paths (unix slashes) with imported types + * used in macros, used for HMR cache busting in @vitejs/plugin-vue and + * vue-loader. + */ + deps?: string[] } export interface SFCStyleBlock extends SFCBlock { diff --git a/packages/compiler-sfc/src/script/context.ts b/packages/compiler-sfc/src/script/context.ts index ec6bbe8d0..1f9658450 100644 --- a/packages/compiler-sfc/src/script/context.ts +++ b/packages/compiler-sfc/src/script/context.ts @@ -7,7 +7,7 @@ import { PropsDestructureBindings } from './defineProps' import { ModelDecl } from './defineModel' import { BindingMetadata } from '../../../compiler-core/src' import MagicString from 'magic-string' -import { TypeScope, WithScope } from './resolveType' +import { TypeScope } from './resolveType' export class ScriptCompileContext { isJS: boolean @@ -56,13 +56,17 @@ export class ScriptCompileContext { // codegen bindingMetadata: BindingMetadata = {} - helperImports: Set = new Set() helper(key: string): string { this.helperImports.add(key) return `_${key}` } + /** + * to be exposed on compiled script block for HMR cache busting + */ + deps?: string[] + constructor( public descriptor: SFCDescriptor, public options: SFCScriptCompileOptions @@ -125,7 +129,7 @@ export class ScriptCompileContext { return block.content.slice(node.start!, node.end!) } - error(msg: string, node: Node & WithScope, scope?: TypeScope): never { + error(msg: string, node: Node, scope?: TypeScope): never { const offset = scope ? scope.offset : this.startOffset! throw new Error( `[@vue/compiler-sfc] ${msg}\n\n${ diff --git a/packages/compiler-sfc/src/script/resolveType.ts b/packages/compiler-sfc/src/script/resolveType.ts index 9d306d7bc..bbbbd4c5b 100644 --- a/packages/compiler-sfc/src/script/resolveType.ts +++ b/packages/compiler-sfc/src/script/resolveType.ts @@ -134,6 +134,7 @@ function innerResolveTypeElements( break } } else { + // TODO support `number` and `string` index type when possible ctx.error( `Unsupported index type: ${node.indexType.type}`, node.indexType, @@ -320,7 +321,11 @@ function resolveStringType( case 'Uncapitalize': return getParam().map(s => s[0].toLowerCase() + s.slice(1)) default: - ctx.error('Failed to resolve type reference', node, scope) + ctx.error( + 'Unsupported type when resolving string type', + node.typeName, + scope + ) } } } @@ -906,7 +911,7 @@ export function inferRuntimeType( if (node.typeName.type === 'Identifier') { const resolved = resolveTypeReference(ctx, node, scope) if (resolved) { - return inferRuntimeType(ctx, resolved, scope) + return inferRuntimeType(ctx, resolved, resolved._ownerScope) } switch (node.typeName.name) { case 'Array': @@ -988,9 +993,15 @@ export function inferRuntimeType( 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]) + try { + const resolved = resolveTypeElements(ctx, node.objectType, scope) + const key = node.indexType.literal.value + const prop = resolved.props[key] + return inferRuntimeType(ctx, prop, prop._ownerScope) + } catch (e) { + // avoid hard error, fallback to unknown + return [UNKNOWN_TYPE] + } } }