-import { TSTypeAliasDeclaration } from '@babel/types'
+import { Identifier } from '@babel/types'
import { parse } from '../../src'
import { ScriptCompileContext } from '../../src/script/context'
import {
describe('resolveType', () => {
test('type literal', () => {
- const { props, calls } = resolve(`type Target = {
+ const { props, calls } = resolve(`defineProps<{
foo: number // property
bar(): void // method
'baz': string // string literal key
(e: 'foo'): void // call signature
(e: 'bar'): void
- }`)
+ }>()`)
expect(props).toStrictEqual({
foo: ['Number'],
bar: ['Function'],
expect(
resolve(`
type Aliased = { foo: number }
- type Target = Aliased
+ defineProps<Aliased>()
`).props
).toStrictEqual({
foo: ['Number']
expect(
resolve(`
export type Aliased = { foo: number }
- type Target = Aliased
+ defineProps<Aliased>()
`).props
).toStrictEqual({
foo: ['Number']
expect(
resolve(`
interface Aliased { foo: number }
- type Target = Aliased
+ defineProps<Aliased>()
`).props
).toStrictEqual({
foo: ['Number']
expect(
resolve(`
export interface Aliased { foo: number }
- type Target = Aliased
+ defineProps<Aliased>()
`).props
).toStrictEqual({
foo: ['Number']
export interface B extends A { b: boolean }
interface C { c: string }
interface Aliased extends B, C { foo: number }
- type Target = Aliased
+ defineProps<Aliased>()
`).props
).toStrictEqual({
a: ['Function'],
expect(
resolve(`
class Foo {}
- type Target = {
- foo: Foo
- }
+ defineProps<{ foo: Foo }>()
`).props
).toStrictEqual({
foo: ['Object']
test('function type', () => {
expect(
resolve(`
- type Target = (e: 'foo') => void
+ defineProps<(e: 'foo') => void>()
`).calls?.length
).toBe(1)
})
expect(
resolve(`
type Fn = (e: 'foo') => void
- type Target = Fn
+ defineProps<Fn>()
`).calls?.length
).toBe(1)
})
type Foo = { foo: number }
type Bar = { bar: string }
type Baz = { bar: string | boolean }
- type Target = { self: any } & Foo & Bar & Baz
+ defineProps<{ self: any } & Foo & Bar & Baz>()
`).props
).toStrictEqual({
self: ['Unknown'],
note: string
}
- type Target = CommonProps & ConditionalProps
+ defineProps<CommonProps & ConditionalProps>()
`).props
).toStrictEqual({
size: ['String'],
resolve(`
type T = 'foo' | 'bar'
type S = 'x' | 'y'
- type Target = {
+ defineProps<{
[\`_\${T}_\${S}_\`]: string
- }
+ }>()
`).props
).toStrictEqual({
_foo_x_: ['String'],
expect(
resolve(`
type T = 'foo' | 'bar'
- type Target = { [K in T]: string | number } & {
+ defineProps<{ [K in T]: string | number } & {
[K in 'optional']?: boolean
} & {
[K in Capitalize<T>]: string
[K in Uppercase<Extract<T, 'foo'>>]: string
} & {
[K in \`x\${T}\`]: string
- }
+ }>()
`).props
).toStrictEqual({
foo: ['String', 'Number'],
resolve(`
type T = { foo: number, bar: string, baz: boolean }
type K = 'foo' | 'bar'
- type Target = Pick<T, K>
+ defineProps<Pick<T, K>>()
`).props
).toStrictEqual({
foo: ['Number'],
resolve(`
type T = { foo: number, bar: string, baz: boolean }
type K = 'foo' | 'bar'
- type Target = Omit<T, K>
+ defineProps<Omit<T, K>>()
`).props
).toStrictEqual({
baz: ['Boolean']
resolve(`
type T = { bar: number }
type S = { nested: { foo: T['bar'] }}
- type Target = S['nested']
+ defineProps<S['nested']>()
`).props
).toStrictEqual({
foo: ['Number']
}
}
}
- type Target = Foo.Bar.A
+ defineProps<Foo.Bar.A>()
`).props
).toStrictEqual({
foo: ['Number']
`
import { P } from './foo'
import { Y as PP } from './bar'
- type Target = P & PP
+ defineProps<P & PP>()
`,
{
'/foo.ts': 'export type P = { foo: number }',
`
import { P } from './foo.vue'
import { P as PP } from './bar.vue'
- type Target = P & PP
+ defineProps<P & PP>()
`,
{
'/foo.vue':
resolve(
`
import { P } from './foo'
- type Target = P
+ defineProps<P>()
`,
{
'/foo.ts': `import type { P as PP } from './nested/bar.vue'
resolve(
`
import { PP as P } from './foo'
- type Target = P
+ defineProps<P>()
`,
{
'/foo.ts': `export { P as PP } from './bar'`,
})
test('ts module resolve', () => {
- expect(
- resolve(
- `
+ const files = {
+ '/node_modules/foo/package.json': JSON.stringify({
+ types: 'index.d.ts'
+ }),
+ '/node_modules/foo/index.d.ts': 'export type P = { foo: number }',
+ '/tsconfig.json': JSON.stringify({
+ compilerOptions: {
+ paths: {
+ bar: ['./pp.ts']
+ }
+ }
+ }),
+ '/pp.ts': 'export type PP = { bar: string }'
+ }
+
+ const { props } = resolve(
+ `
import { P } from 'foo'
import { PP } from 'bar'
- type Target = P & PP
+ defineProps<P & PP>()
`,
- {
- '/node_modules/foo/package.json': JSON.stringify({
- name: 'foo',
- version: '1.0.0',
- types: 'index.d.ts'
- }),
- '/node_modules/foo/index.d.ts': 'export type P = { foo: number }',
- '/tsconfig.json': JSON.stringify({
- compilerOptions: {
- paths: {
- bar: ['./other/bar.ts']
- }
- }
- }),
- '/other/bar.ts': 'export type PP = { bar: string }'
- }
- ).props
- ).toStrictEqual({
+ files
+ )
+
+ expect(props).toStrictEqual({
foo: ['Number'],
bar: ['String']
})
describe('errors', () => {
test('failed type reference', () => {
- expect(() => resolve(`type Target = X`)).toThrow(
+ expect(() => resolve(`defineProps<X>()`)).toThrow(
`Unresolvable type reference`
)
})
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`
)
})
test('unsupported index type', () => {
- expect(() => resolve(`type Target = X[K]`)).toThrow(
+ expect(() => resolve(`defineProps<X[K]>()`)).toThrow(
`Unsupported index type`
)
})
test('failed improt source resolve', () => {
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`)
})
})
// skipping that here, so need to manually register imports
ctx.userImports = recordImports(ctx.scriptSetupAst!.body) as any
- const targetDecl = ctx.scriptSetupAst!.body.find(
- s => s.type === 'TSTypeAliasDeclaration' && s.id.name === 'Target'
- ) as TSTypeAliasDeclaration
- const raw = resolveTypeElements(ctx, targetDecl.typeAnnotation)
+ let target: any
+ for (const s of ctx.scriptSetupAst!.body) {
+ if (
+ 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[]> = {}
for (const key in raw.props) {
props[key] = inferRuntimeType(ctx, raw.props[key])