mirror of https://github.com/vuejs/core.git
refactor: adjust ResolvedElements shape
This commit is contained in:
parent
1cfab4c695
commit
51773d5d1d
|
@ -8,19 +8,19 @@ import {
|
||||||
|
|
||||||
describe('resolveType', () => {
|
describe('resolveType', () => {
|
||||||
test('type literal', () => {
|
test('type literal', () => {
|
||||||
const { elements, callSignatures } = resolve(`type Target = {
|
const { props, calls } = resolve(`type Target = {
|
||||||
foo: number // property
|
foo: number // property
|
||||||
bar(): void // method
|
bar(): void // method
|
||||||
'baz': string // string literal key
|
'baz': string // string literal key
|
||||||
(e: 'foo'): void // call signature
|
(e: 'foo'): void // call signature
|
||||||
(e: 'bar'): void
|
(e: 'bar'): void
|
||||||
}`)
|
}`)
|
||||||
expect(elements).toStrictEqual({
|
expect(props).toStrictEqual({
|
||||||
foo: ['Number'],
|
foo: ['Number'],
|
||||||
bar: ['Function'],
|
bar: ['Function'],
|
||||||
baz: ['String']
|
baz: ['String']
|
||||||
})
|
})
|
||||||
expect(callSignatures?.length).toBe(2)
|
expect(calls?.length).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('reference type', () => {
|
test('reference type', () => {
|
||||||
|
@ -28,7 +28,7 @@ describe('resolveType', () => {
|
||||||
resolve(`
|
resolve(`
|
||||||
type Aliased = { foo: number }
|
type Aliased = { foo: number }
|
||||||
type Target = Aliased
|
type Target = Aliased
|
||||||
`).elements
|
`).props
|
||||||
).toStrictEqual({
|
).toStrictEqual({
|
||||||
foo: ['Number']
|
foo: ['Number']
|
||||||
})
|
})
|
||||||
|
@ -39,7 +39,7 @@ describe('resolveType', () => {
|
||||||
resolve(`
|
resolve(`
|
||||||
export type Aliased = { foo: number }
|
export type Aliased = { foo: number }
|
||||||
type Target = Aliased
|
type Target = Aliased
|
||||||
`).elements
|
`).props
|
||||||
).toStrictEqual({
|
).toStrictEqual({
|
||||||
foo: ['Number']
|
foo: ['Number']
|
||||||
})
|
})
|
||||||
|
@ -50,7 +50,7 @@ describe('resolveType', () => {
|
||||||
resolve(`
|
resolve(`
|
||||||
interface Aliased { foo: number }
|
interface Aliased { foo: number }
|
||||||
type Target = Aliased
|
type Target = Aliased
|
||||||
`).elements
|
`).props
|
||||||
).toStrictEqual({
|
).toStrictEqual({
|
||||||
foo: ['Number']
|
foo: ['Number']
|
||||||
})
|
})
|
||||||
|
@ -61,7 +61,7 @@ describe('resolveType', () => {
|
||||||
resolve(`
|
resolve(`
|
||||||
export interface Aliased { foo: number }
|
export interface Aliased { foo: number }
|
||||||
type Target = Aliased
|
type Target = Aliased
|
||||||
`).elements
|
`).props
|
||||||
).toStrictEqual({
|
).toStrictEqual({
|
||||||
foo: ['Number']
|
foo: ['Number']
|
||||||
})
|
})
|
||||||
|
@ -75,7 +75,7 @@ describe('resolveType', () => {
|
||||||
interface C { c: string }
|
interface C { c: string }
|
||||||
interface Aliased extends B, C { foo: number }
|
interface Aliased extends B, C { foo: number }
|
||||||
type Target = Aliased
|
type Target = Aliased
|
||||||
`).elements
|
`).props
|
||||||
).toStrictEqual({
|
).toStrictEqual({
|
||||||
a: ['Function'],
|
a: ['Function'],
|
||||||
b: ['Boolean'],
|
b: ['Boolean'],
|
||||||
|
@ -88,7 +88,7 @@ describe('resolveType', () => {
|
||||||
expect(
|
expect(
|
||||||
resolve(`
|
resolve(`
|
||||||
type Target = (e: 'foo') => void
|
type Target = (e: 'foo') => void
|
||||||
`).callSignatures?.length
|
`).calls?.length
|
||||||
).toBe(1)
|
).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ describe('resolveType', () => {
|
||||||
resolve(`
|
resolve(`
|
||||||
type Fn = (e: 'foo') => void
|
type Fn = (e: 'foo') => void
|
||||||
type Target = Fn
|
type Target = Fn
|
||||||
`).callSignatures?.length
|
`).calls?.length
|
||||||
).toBe(1)
|
).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ describe('resolveType', () => {
|
||||||
type Bar = { bar: string }
|
type Bar = { bar: string }
|
||||||
type Baz = { bar: string | boolean }
|
type Baz = { bar: string | boolean }
|
||||||
type Target = { self: any } & Foo & Bar & Baz
|
type Target = { self: any } & Foo & Bar & Baz
|
||||||
`).elements
|
`).props
|
||||||
).toStrictEqual({
|
).toStrictEqual({
|
||||||
self: ['Unknown'],
|
self: ['Unknown'],
|
||||||
foo: ['Number'],
|
foo: ['Number'],
|
||||||
|
@ -138,7 +138,7 @@ describe('resolveType', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Target = CommonProps & ConditionalProps
|
type Target = CommonProps & ConditionalProps
|
||||||
`).elements
|
`).props
|
||||||
).toStrictEqual({
|
).toStrictEqual({
|
||||||
size: ['String'],
|
size: ['String'],
|
||||||
color: ['String', 'Number'],
|
color: ['String', 'Number'],
|
||||||
|
@ -155,7 +155,7 @@ describe('resolveType', () => {
|
||||||
type Target = {
|
type Target = {
|
||||||
[\`_\${T}_\${S}_\`]: string
|
[\`_\${T}_\${S}_\`]: string
|
||||||
}
|
}
|
||||||
`).elements
|
`).props
|
||||||
).toStrictEqual({
|
).toStrictEqual({
|
||||||
_foo_x_: ['String'],
|
_foo_x_: ['String'],
|
||||||
_foo_y_: ['String'],
|
_foo_y_: ['String'],
|
||||||
|
@ -177,7 +177,7 @@ describe('resolveType', () => {
|
||||||
} & {
|
} & {
|
||||||
[K in \`x\${T}\`]: string
|
[K in \`x\${T}\`]: string
|
||||||
}
|
}
|
||||||
`).elements
|
`).props
|
||||||
).toStrictEqual({
|
).toStrictEqual({
|
||||||
foo: ['String', 'Number'],
|
foo: ['String', 'Number'],
|
||||||
bar: ['String', 'Number'],
|
bar: ['String', 'Number'],
|
||||||
|
@ -196,7 +196,7 @@ describe('resolveType', () => {
|
||||||
type T = { foo: number, bar: string, baz: boolean }
|
type T = { foo: number, bar: string, baz: boolean }
|
||||||
type K = 'foo' | 'bar'
|
type K = 'foo' | 'bar'
|
||||||
type Target = Pick<T, K>
|
type Target = Pick<T, K>
|
||||||
`).elements
|
`).props
|
||||||
).toStrictEqual({
|
).toStrictEqual({
|
||||||
foo: ['Number'],
|
foo: ['Number'],
|
||||||
bar: ['String']
|
bar: ['String']
|
||||||
|
@ -209,7 +209,7 @@ describe('resolveType', () => {
|
||||||
type T = { foo: number, bar: string, baz: boolean }
|
type T = { foo: number, bar: string, baz: boolean }
|
||||||
type K = 'foo' | 'bar'
|
type K = 'foo' | 'bar'
|
||||||
type Target = Omit<T, K>
|
type Target = Omit<T, K>
|
||||||
`).elements
|
`).props
|
||||||
).toStrictEqual({
|
).toStrictEqual({
|
||||||
baz: ['Boolean']
|
baz: ['Boolean']
|
||||||
})
|
})
|
||||||
|
@ -231,13 +231,13 @@ function resolve(code: string) {
|
||||||
s => s.type === 'TSTypeAliasDeclaration' && s.id.name === 'Target'
|
s => s.type === 'TSTypeAliasDeclaration' && s.id.name === 'Target'
|
||||||
) as TSTypeAliasDeclaration
|
) as TSTypeAliasDeclaration
|
||||||
const raw = resolveTypeElements(ctx, targetDecl.typeAnnotation)
|
const raw = resolveTypeElements(ctx, targetDecl.typeAnnotation)
|
||||||
const elements: Record<string, string[]> = {}
|
const props: Record<string, string[]> = {}
|
||||||
for (const key in raw) {
|
for (const key in raw.props) {
|
||||||
elements[key] = inferRuntimeType(ctx, raw[key])
|
props[key] = inferRuntimeType(ctx, raw.props[key])
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
elements,
|
props,
|
||||||
callSignatures: raw.__callSignatures,
|
calls: raw.calls,
|
||||||
raw
|
raw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,22 +69,22 @@ function extractRuntimeEmits(ctx: ScriptCompileContext): Set<string> {
|
||||||
return emits
|
return emits
|
||||||
}
|
}
|
||||||
|
|
||||||
const elements = resolveTypeElements(ctx, node)
|
const { props, calls } = resolveTypeElements(ctx, node)
|
||||||
|
|
||||||
let hasProperty = false
|
let hasProperty = false
|
||||||
for (const key in elements) {
|
for (const key in props) {
|
||||||
emits.add(key)
|
emits.add(key)
|
||||||
hasProperty = true
|
hasProperty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elements.__callSignatures) {
|
if (calls) {
|
||||||
if (hasProperty) {
|
if (hasProperty) {
|
||||||
ctx.error(
|
ctx.error(
|
||||||
`defineEmits() type cannot mixed call signature and property syntax.`,
|
`defineEmits() type cannot mixed call signature and property syntax.`,
|
||||||
node
|
node
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
for (const call of elements.__callSignatures) {
|
for (const call of calls) {
|
||||||
extractEventNames(call.parameters[0], emits)
|
extractEventNames(call.parameters[0], emits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,8 +191,8 @@ function resolveRuntimePropsFromType(
|
||||||
): PropTypeData[] {
|
): PropTypeData[] {
|
||||||
const props: PropTypeData[] = []
|
const props: PropTypeData[] = []
|
||||||
const elements = resolveTypeElements(ctx, node)
|
const elements = resolveTypeElements(ctx, node)
|
||||||
for (const key in elements) {
|
for (const key in elements.props) {
|
||||||
const e = elements[key]
|
const e = elements.props[key]
|
||||||
let type = inferRuntimeType(ctx, e)
|
let type = inferRuntimeType(ctx, e)
|
||||||
let skipCheck = false
|
let skipCheck = false
|
||||||
// skip check for result containing unknown types
|
// skip check for result containing unknown types
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { UNKNOWN_TYPE } from './utils'
|
||||||
import { ScriptCompileContext } from './context'
|
import { ScriptCompileContext } from './context'
|
||||||
import { ImportBinding } from '../compileScript'
|
import { ImportBinding } from '../compileScript'
|
||||||
import { TSInterfaceDeclaration } from '@babel/types'
|
import { TSInterfaceDeclaration } from '@babel/types'
|
||||||
import { capitalize, hasOwn, isArray } from '@vue/shared'
|
import { capitalize, hasOwn } from '@vue/shared'
|
||||||
import { Expression } from '@babel/types'
|
import { Expression } from '@babel/types'
|
||||||
|
|
||||||
export interface TypeScope {
|
export interface TypeScope {
|
||||||
|
@ -28,11 +28,9 @@ export interface TypeScope {
|
||||||
types: Record<string, Node>
|
types: Record<string, Node>
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResolvedElements = Record<
|
interface ResolvedElements {
|
||||||
string,
|
props: Record<string, TSPropertySignature | TSMethodSignature>
|
||||||
TSPropertySignature | TSMethodSignature
|
calls?: (TSCallSignatureDeclaration | TSFunctionType)[]
|
||||||
> & {
|
|
||||||
__callSignatures?: (TSCallSignatureDeclaration | TSFunctionType)[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,9 +60,7 @@ function innerResolveTypeElements(
|
||||||
case 'TSParenthesizedType':
|
case 'TSParenthesizedType':
|
||||||
return resolveTypeElements(ctx, node.typeAnnotation)
|
return resolveTypeElements(ctx, node.typeAnnotation)
|
||||||
case 'TSFunctionType': {
|
case 'TSFunctionType': {
|
||||||
const ret: ResolvedElements = {}
|
return { props: {}, calls: [node] }
|
||||||
addCallSignature(ret, node)
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
case 'TSExpressionWithTypeArguments': // referenced by interface extends
|
case 'TSExpressionWithTypeArguments': // referenced by interface extends
|
||||||
case 'TSTypeReference': {
|
case 'TSTypeReference': {
|
||||||
|
@ -98,32 +94,11 @@ function innerResolveTypeElements(
|
||||||
ctx.error(`Unsupported type in SFC macro: ${node.type}`, node)
|
ctx.error(`Unsupported type in SFC macro: ${node.type}`, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
function addCallSignature(
|
|
||||||
elements: ResolvedElements,
|
|
||||||
node:
|
|
||||||
| TSCallSignatureDeclaration
|
|
||||||
| TSFunctionType
|
|
||||||
| (TSCallSignatureDeclaration | TSFunctionType)[]
|
|
||||||
) {
|
|
||||||
if (!elements.__callSignatures) {
|
|
||||||
Object.defineProperty(elements, '__callSignatures', {
|
|
||||||
enumerable: false,
|
|
||||||
value: isArray(node) ? node : [node]
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
if (isArray(node)) {
|
|
||||||
elements.__callSignatures.push(...node)
|
|
||||||
} else {
|
|
||||||
elements.__callSignatures.push(node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function typeElementsToMap(
|
function typeElementsToMap(
|
||||||
ctx: ScriptCompileContext,
|
ctx: ScriptCompileContext,
|
||||||
elements: TSTypeElement[]
|
elements: TSTypeElement[]
|
||||||
): ResolvedElements {
|
): ResolvedElements {
|
||||||
const ret: ResolvedElements = {}
|
const res: ResolvedElements = { props: {} }
|
||||||
for (const e of elements) {
|
for (const e of elements) {
|
||||||
if (e.type === 'TSPropertySignature' || e.type === 'TSMethodSignature') {
|
if (e.type === 'TSPropertySignature' || e.type === 'TSMethodSignature') {
|
||||||
const name =
|
const name =
|
||||||
|
@ -133,10 +108,10 @@ function typeElementsToMap(
|
||||||
? e.key.value
|
? e.key.value
|
||||||
: null
|
: null
|
||||||
if (name && !e.computed) {
|
if (name && !e.computed) {
|
||||||
ret[name] = e
|
res.props[name] = e
|
||||||
} else if (e.key.type === 'TemplateLiteral') {
|
} else if (e.key.type === 'TemplateLiteral') {
|
||||||
for (const key of resolveTemplateKeys(ctx, e.key)) {
|
for (const key of resolveTemplateKeys(ctx, e.key)) {
|
||||||
ret[key] = e
|
res.props[key] = e
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.error(
|
ctx.error(
|
||||||
|
@ -145,31 +120,32 @@ function typeElementsToMap(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (e.type === 'TSCallSignatureDeclaration') {
|
} else if (e.type === 'TSCallSignatureDeclaration') {
|
||||||
addCallSignature(ret, e)
|
;(res.calls || (res.calls = [])).push(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeElements(
|
function mergeElements(
|
||||||
maps: ResolvedElements[],
|
maps: ResolvedElements[],
|
||||||
type: 'TSUnionType' | 'TSIntersectionType'
|
type: 'TSUnionType' | 'TSIntersectionType'
|
||||||
): ResolvedElements {
|
): ResolvedElements {
|
||||||
const res: ResolvedElements = Object.create(null)
|
const res: ResolvedElements = { props: {} }
|
||||||
for (const m of maps) {
|
const { props: baseProps } = res
|
||||||
for (const key in m) {
|
for (const { props, calls } of maps) {
|
||||||
if (!(key in res)) {
|
for (const key in props) {
|
||||||
res[key] = m[key]
|
if (!hasOwn(baseProps, key)) {
|
||||||
|
baseProps[key] = props[key]
|
||||||
} else {
|
} else {
|
||||||
res[key] = createProperty(res[key].key, {
|
baseProps[key] = createProperty(baseProps[key].key, {
|
||||||
type,
|
type,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
types: [res[key], m[key]]
|
types: [baseProps[key], props[key]]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m.__callSignatures) {
|
if (calls) {
|
||||||
addCallSignature(res, m.__callSignatures)
|
;(res.calls || (res.calls = [])).push(...calls)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
@ -197,10 +173,10 @@ function resolveInterfaceMembers(
|
||||||
const base = typeElementsToMap(ctx, node.body.body)
|
const base = typeElementsToMap(ctx, node.body.body)
|
||||||
if (node.extends) {
|
if (node.extends) {
|
||||||
for (const ext of node.extends) {
|
for (const ext of node.extends) {
|
||||||
const resolvedExt = resolveTypeElements(ctx, ext)
|
const { props } = resolveTypeElements(ctx, ext)
|
||||||
for (const key in resolvedExt) {
|
for (const key in props) {
|
||||||
if (!hasOwn(base, key)) {
|
if (!hasOwn(base.props, key)) {
|
||||||
base[key] = resolvedExt[key]
|
base.props[key] = props[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,13 +188,13 @@ function resolveMappedType(
|
||||||
ctx: ScriptCompileContext,
|
ctx: ScriptCompileContext,
|
||||||
node: TSMappedType
|
node: TSMappedType
|
||||||
): ResolvedElements {
|
): ResolvedElements {
|
||||||
const res: ResolvedElements = {}
|
const res: ResolvedElements = { props: {} }
|
||||||
if (!node.typeParameter.constraint) {
|
if (!node.typeParameter.constraint) {
|
||||||
ctx.error(`mapped type used in macros must have a finite constraint.`, node)
|
ctx.error(`mapped type used in macros must have a finite constraint.`, node)
|
||||||
}
|
}
|
||||||
const keys = resolveStringType(ctx, node.typeParameter.constraint)
|
const keys = resolveStringType(ctx, node.typeParameter.constraint)
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
res[key] = createProperty(
|
res.props[key] = createProperty(
|
||||||
{
|
{
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
name: key
|
name: key
|
||||||
|
@ -323,20 +299,18 @@ function resolveBuiltin(
|
||||||
return t
|
return t
|
||||||
case 'Pick': {
|
case 'Pick': {
|
||||||
const picked = resolveStringType(ctx, node.typeParameters!.params[1])
|
const picked = resolveStringType(ctx, node.typeParameters!.params[1])
|
||||||
const res: ResolvedElements = {}
|
const res: ResolvedElements = { props: {}, calls: t.calls }
|
||||||
if (t.__callSignatures) addCallSignature(res, t.__callSignatures)
|
|
||||||
for (const key of picked) {
|
for (const key of picked) {
|
||||||
res[key] = t[key]
|
res.props[key] = t.props[key]
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
case 'Omit':
|
case 'Omit':
|
||||||
const omitted = resolveStringType(ctx, node.typeParameters!.params[1])
|
const omitted = resolveStringType(ctx, node.typeParameters!.params[1])
|
||||||
const res: ResolvedElements = {}
|
const res: ResolvedElements = { props: {}, calls: t.calls }
|
||||||
if (t.__callSignatures) addCallSignature(res, t.__callSignatures)
|
for (const key in t.props) {
|
||||||
for (const key in t) {
|
|
||||||
if (!omitted.includes(key)) {
|
if (!omitted.includes(key)) {
|
||||||
res[key] = t[key]
|
res.props[key] = t.props[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
|
Loading…
Reference in New Issue