test: refactor resolveType test

This commit is contained in:
Evan You 2023-04-14 17:43:14 +08:00
parent 3982bef533
commit 34a007d00d
1 changed files with 62 additions and 57 deletions

View File

@ -1,4 +1,4 @@
import { TSTypeAliasDeclaration } from '@babel/types' import { Identifier } from '@babel/types'
import { parse } from '../../src' import { parse } from '../../src'
import { ScriptCompileContext } from '../../src/script/context' import { ScriptCompileContext } from '../../src/script/context'
import { import {
@ -14,13 +14,13 @@ registerTS(ts)
describe('resolveType', () => { describe('resolveType', () => {
test('type literal', () => { test('type literal', () => {
const { props, calls } = resolve(`type Target = { const { props, calls } = resolve(`defineProps<{
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(props).toStrictEqual({ expect(props).toStrictEqual({
foo: ['Number'], foo: ['Number'],
bar: ['Function'], bar: ['Function'],
@ -33,7 +33,7 @@ describe('resolveType', () => {
expect( expect(
resolve(` resolve(`
type Aliased = { foo: number } type Aliased = { foo: number }
type Target = Aliased defineProps<Aliased>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
foo: ['Number'] foo: ['Number']
@ -44,7 +44,7 @@ describe('resolveType', () => {
expect( expect(
resolve(` resolve(`
export type Aliased = { foo: number } export type Aliased = { foo: number }
type Target = Aliased defineProps<Aliased>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
foo: ['Number'] foo: ['Number']
@ -55,7 +55,7 @@ describe('resolveType', () => {
expect( expect(
resolve(` resolve(`
interface Aliased { foo: number } interface Aliased { foo: number }
type Target = Aliased defineProps<Aliased>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
foo: ['Number'] foo: ['Number']
@ -66,7 +66,7 @@ describe('resolveType', () => {
expect( expect(
resolve(` resolve(`
export interface Aliased { foo: number } export interface Aliased { foo: number }
type Target = Aliased defineProps<Aliased>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
foo: ['Number'] foo: ['Number']
@ -80,7 +80,7 @@ describe('resolveType', () => {
export interface B extends A { b: boolean } export interface B extends A { b: boolean }
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 defineProps<Aliased>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
a: ['Function'], a: ['Function'],
@ -94,9 +94,7 @@ describe('resolveType', () => {
expect( expect(
resolve(` resolve(`
class Foo {} class Foo {}
type Target = { defineProps<{ foo: Foo }>()
foo: Foo
}
`).props `).props
).toStrictEqual({ ).toStrictEqual({
foo: ['Object'] foo: ['Object']
@ -106,7 +104,7 @@ describe('resolveType', () => {
test('function type', () => { test('function type', () => {
expect( expect(
resolve(` resolve(`
type Target = (e: 'foo') => void defineProps<(e: 'foo') => void>()
`).calls?.length `).calls?.length
).toBe(1) ).toBe(1)
}) })
@ -115,7 +113,7 @@ describe('resolveType', () => {
expect( expect(
resolve(` resolve(`
type Fn = (e: 'foo') => void type Fn = (e: 'foo') => void
type Target = Fn defineProps<Fn>()
`).calls?.length `).calls?.length
).toBe(1) ).toBe(1)
}) })
@ -126,7 +124,7 @@ describe('resolveType', () => {
type Foo = { foo: number } type Foo = { foo: number }
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 defineProps<{ self: any } & Foo & Bar & Baz>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
self: ['Unknown'], self: ['Unknown'],
@ -156,7 +154,7 @@ describe('resolveType', () => {
note: string note: string
} }
type Target = CommonProps & ConditionalProps defineProps<CommonProps & ConditionalProps>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
size: ['String'], size: ['String'],
@ -171,9 +169,9 @@ describe('resolveType', () => {
resolve(` resolve(`
type T = 'foo' | 'bar' type T = 'foo' | 'bar'
type S = 'x' | 'y' type S = 'x' | 'y'
type Target = { defineProps<{
[\`_\${T}_\${S}_\`]: string [\`_\${T}_\${S}_\`]: string
} }>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
_foo_x_: ['String'], _foo_x_: ['String'],
@ -187,7 +185,7 @@ describe('resolveType', () => {
expect( expect(
resolve(` resolve(`
type T = 'foo' | 'bar' type T = 'foo' | 'bar'
type Target = { [K in T]: string | number } & { defineProps<{ [K in T]: string | number } & {
[K in 'optional']?: boolean [K in 'optional']?: boolean
} & { } & {
[K in Capitalize<T>]: string [K in Capitalize<T>]: string
@ -195,7 +193,7 @@ describe('resolveType', () => {
[K in Uppercase<Extract<T, 'foo'>>]: string [K in Uppercase<Extract<T, 'foo'>>]: string
} & { } & {
[K in \`x\${T}\`]: string [K in \`x\${T}\`]: string
} }>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
foo: ['String', 'Number'], foo: ['String', 'Number'],
@ -214,7 +212,7 @@ describe('resolveType', () => {
resolve(` resolve(`
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> defineProps<Pick<T, K>>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
foo: ['Number'], foo: ['Number'],
@ -227,7 +225,7 @@ describe('resolveType', () => {
resolve(` resolve(`
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> defineProps<Omit<T, K>>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
baz: ['Boolean'] baz: ['Boolean']
@ -239,7 +237,7 @@ describe('resolveType', () => {
resolve(` resolve(`
type T = { bar: number } type T = { bar: number }
type S = { nested: { foo: T['bar'] }} type S = { nested: { foo: T['bar'] }}
type Target = S['nested'] defineProps<S['nested']>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
foo: ['Number'] foo: ['Number']
@ -258,7 +256,7 @@ describe('resolveType', () => {
} }
} }
} }
type Target = Foo.Bar.A defineProps<Foo.Bar.A>()
`).props `).props
).toStrictEqual({ ).toStrictEqual({
foo: ['Number'] foo: ['Number']
@ -272,7 +270,7 @@ describe('resolveType', () => {
` `
import { P } from './foo' import { P } from './foo'
import { Y as PP } from './bar' import { Y as PP } from './bar'
type Target = P & PP defineProps<P & PP>()
`, `,
{ {
'/foo.ts': 'export type P = { foo: number }', '/foo.ts': 'export type P = { foo: number }',
@ -291,7 +289,7 @@ describe('resolveType', () => {
` `
import { P } from './foo.vue' import { P } from './foo.vue'
import { P as PP } from './bar.vue' import { P as PP } from './bar.vue'
type Target = P & PP defineProps<P & PP>()
`, `,
{ {
'/foo.vue': '/foo.vue':
@ -311,7 +309,7 @@ describe('resolveType', () => {
resolve( resolve(
` `
import { P } from './foo' import { P } from './foo'
type Target = P defineProps<P>()
`, `,
{ {
'/foo.ts': `import type { P as PP } from './nested/bar.vue' '/foo.ts': `import type { P as PP } from './nested/bar.vue'
@ -331,7 +329,7 @@ describe('resolveType', () => {
resolve( resolve(
` `
import { PP as P } from './foo' import { PP as P } from './foo'
type Target = P defineProps<P>()
`, `,
{ {
'/foo.ts': `export { P as PP } from './bar'`, '/foo.ts': `export { P as PP } from './bar'`,
@ -344,31 +342,31 @@ describe('resolveType', () => {
}) })
test('ts module resolve', () => { test('ts module resolve', () => {
expect( const files = {
resolve(
`
import { P } from 'foo'
import { PP } from 'bar'
type Target = P & PP
`,
{
'/node_modules/foo/package.json': JSON.stringify({ '/node_modules/foo/package.json': JSON.stringify({
name: 'foo',
version: '1.0.0',
types: 'index.d.ts' types: 'index.d.ts'
}), }),
'/node_modules/foo/index.d.ts': 'export type P = { foo: number }', '/node_modules/foo/index.d.ts': 'export type P = { foo: number }',
'/tsconfig.json': JSON.stringify({ '/tsconfig.json': JSON.stringify({
compilerOptions: { compilerOptions: {
paths: { paths: {
bar: ['./other/bar.ts'] bar: ['./pp.ts']
} }
} }
}), }),
'/other/bar.ts': 'export type PP = { bar: string }' '/pp.ts': 'export type PP = { bar: string }'
} }
).props
).toStrictEqual({ const { props } = resolve(
`
import { P } from 'foo'
import { PP } from 'bar'
defineProps<P & PP>()
`,
files
)
expect(props).toStrictEqual({
foo: ['Number'], foo: ['Number'],
bar: ['String'] bar: ['String']
}) })
@ -377,26 +375,26 @@ describe('resolveType', () => {
describe('errors', () => { describe('errors', () => {
test('failed type reference', () => { test('failed type reference', () => {
expect(() => resolve(`type Target = X`)).toThrow( expect(() => resolve(`defineProps<X>()`)).toThrow(
`Unresolvable type reference` `Unresolvable type reference`
) )
}) })
test('unsupported computed keys', () => { test('unsupported computed keys', () => {
expect(() => resolve(`type Target = { [Foo]: string }`)).toThrow( expect(() => resolve(`defineProps<{ [Foo]: string }>()`)).toThrow(
`Unsupported computed key in type referenced by a macro` `Unsupported computed key in type referenced by a macro`
) )
}) })
test('unsupported index type', () => { test('unsupported index type', () => {
expect(() => resolve(`type Target = X[K]`)).toThrow( expect(() => resolve(`defineProps<X[K]>()`)).toThrow(
`Unsupported index type` `Unsupported index type`
) )
}) })
test('failed improt source resolve', () => { test('failed improt source resolve', () => {
expect(() => expect(() =>
resolve(`import { X } from './foo'; type Target = X`) resolve(`import { X } from './foo'; defineProps<X>()`)
).toThrow(`Failed to resolve import source "./foo" for type X`) ).toThrow(`Failed to resolve import source "./foo" for type X`)
}) })
}) })
@ -426,10 +424,17 @@ function resolve(code: string, files: Record<string, string> = {}) {
// skipping that here, so need to manually register imports // skipping that here, so need to manually register imports
ctx.userImports = recordImports(ctx.scriptSetupAst!.body) as any ctx.userImports = recordImports(ctx.scriptSetupAst!.body) as any
const targetDecl = ctx.scriptSetupAst!.body.find( let target: any
s => s.type === 'TSTypeAliasDeclaration' && s.id.name === 'Target' for (const s of ctx.scriptSetupAst!.body) {
) as TSTypeAliasDeclaration if (
const raw = resolveTypeElements(ctx, targetDecl.typeAnnotation) s.type === 'ExpressionStatement' &&
s.expression.type === 'CallExpression' &&
(s.expression.callee as Identifier).name === 'defineProps'
) {
target = s.expression.typeParameters!.params[0]
}
}
const raw = resolveTypeElements(ctx, target)
const props: Record<string, string[]> = {} const props: Record<string, string[]> = {}
for (const key in raw.props) { for (const key in raw.props) {
props[key] = inferRuntimeType(ctx, raw.props[key]) props[key] = inferRuntimeType(ctx, raw.props[key])