registerTS,
resolveTypeElements,
} from '../../src/script/resolveType'
-
+import { UNKNOWN_TYPE } from '../../src/script/utils'
import ts from 'typescript'
+
registerTS(() => ts)
describe('resolveType', () => {
defineProps<{ self: any } & Foo & Bar & Baz>()
`).props,
).toStrictEqual({
- self: ['Unknown'],
+ self: [UNKNOWN_TYPE],
foo: ['Number'],
// both Bar & Baz has 'bar', but Baz['bar] is wider so it should be
// preferred
const { props } = resolve(
`
import { IMP } from './foo'
- interface Foo { foo: 1, ${1}: 1 }
+ interface Foo { foo: 1, ${1}: 1 }
type Bar = { bar: 1 }
declare const obj: Bar
declare const set: Set<any>
declare const arr: Array<any>
- defineProps<{
+ defineProps<{
imp: keyof IMP,
foo: keyof Foo,
bar: keyof Bar,
})
})
+ test('keyof: index signature', () => {
+ const { props } = resolve(
+ `
+ declare const num: number;
+ interface Foo {
+ [key: symbol]: 1
+ [key: string]: 1
+ [key: typeof num]: 1,
+ }
+
+ type Test<T> = T
+ type Bar = {
+ [key: string]: 1
+ [key: Test<number>]: 1
+ }
+
+ defineProps<{
+ foo: keyof Foo
+ bar: keyof Bar
+ }>()
+ `,
+ )
+
+ expect(props).toStrictEqual({
+ foo: ['Symbol', 'String', 'Number'],
+ bar: [UNKNOWN_TYPE],
+ })
+ })
+
+ test('keyof: utility type', () => {
+ const { props } = resolve(
+ `
+ type Foo = Record<symbol | string, any>
+ type Bar = { [key: string]: any }
+ type AnyRecord = Record<keyof any, any>
+ type Baz = { a: 1, ${1}: 2, b: 3}
+
+ defineProps<{
+ record: keyof Foo,
+ anyRecord: keyof AnyRecord
+ partial: keyof Partial<Bar>,
+ required: keyof Required<Bar>,
+ readonly: keyof Readonly<Bar>,
+ pick: keyof Pick<Baz, 'a' | 1>
+ extract: keyof Extract<keyof Baz, 'a' | 1>
+ }>()
+ `,
+ )
+
+ expect(props).toStrictEqual({
+ record: ['Symbol', 'String'],
+ anyRecord: ['String', 'Number', 'Symbol'],
+ partial: ['String'],
+ required: ['String'],
+ readonly: ['String'],
+ pick: ['String', 'Number'],
+ extract: ['String', 'Number'],
+ })
+ })
+
+ test('keyof: fallback to Unknown', () => {
+ const { props } = resolve(
+ `
+ interface Barr {}
+ interface Bar extends Barr {}
+ type Foo = keyof Bar
+ defineProps<{ foo: Foo }>()
+ `,
+ )
+
+ expect(props).toStrictEqual({
+ foo: [UNKNOWN_TYPE],
+ })
+ })
+
test('ExtractPropTypes (element-plus)', () => {
const { props, raw } = resolve(
`
m.key.type === 'NumericLiteral'
) {
types.add('Number')
+ } else if (m.type === 'TSIndexSignature') {
+ const annotation = m.parameters[0].typeAnnotation
+ if (annotation && annotation.type !== 'Noop') {
+ const type = inferRuntimeType(
+ ctx,
+ annotation.typeAnnotation,
+ scope,
+ )[0]
+ if (type === UNKNOWN_TYPE) return [UNKNOWN_TYPE]
+ types.add(type)
+ }
} else {
types.add('String')
}
}
}
- return types.size ? Array.from(types) : ['Object']
+ return types.size
+ ? Array.from(types)
+ : [isKeyOf ? UNKNOWN_TYPE : 'Object']
}
case 'TSPropertySignature':
if (node.typeAnnotation) {
case 'String':
case 'Array':
case 'ArrayLike':
+ case 'Parameters':
+ case 'ConstructorParameters':
case 'ReadonlyArray':
return ['String', 'Number']
- default:
+
+ // TS built-in utility types
+ case 'Record':
+ case 'Partial':
+ case 'Required':
+ case 'Readonly':
+ if (node.typeParameters && node.typeParameters.params[0]) {
+ return inferRuntimeType(
+ ctx,
+ node.typeParameters.params[0],
+ scope,
+ true,
+ )
+ }
+ break
+ case 'Pick':
+ case 'Extract':
+ if (node.typeParameters && node.typeParameters.params[1]) {
+ return inferRuntimeType(
+ ctx,
+ node.typeParameters.params[1],
+ scope,
+ )
+ }
+ break
+
+ case 'Function':
+ case 'Object':
+ case 'Set':
+ case 'Map':
+ case 'WeakSet':
+ case 'WeakMap':
+ case 'Date':
+ case 'Promise':
+ case 'Error':
+ case 'Uppercase':
+ case 'Lowercase':
+ case 'Capitalize':
+ case 'Uncapitalize':
+ case 'ReadonlyMap':
+ case 'ReadonlySet':
return ['String']
}
- }
+ } else {
+ switch (node.typeName.name) {
+ case 'Array':
+ case 'Function':
+ case 'Object':
+ case 'Set':
+ case 'Map':
+ case 'WeakSet':
+ case 'WeakMap':
+ case 'Date':
+ case 'Promise':
+ case 'Error':
+ return [node.typeName.name]
+
+ // TS built-in utility types
+ // https://www.typescriptlang.org/docs/handbook/utility-types.html
+ case 'Partial':
+ case 'Required':
+ case 'Readonly':
+ case 'Record':
+ case 'Pick':
+ case 'Omit':
+ case 'InstanceType':
+ return ['Object']
+
+ case 'Uppercase':
+ case 'Lowercase':
+ case 'Capitalize':
+ case 'Uncapitalize':
+ return ['String']
- switch (node.typeName.name) {
- case 'Array':
- case 'Function':
- case 'Object':
- case 'Set':
- case 'Map':
- case 'WeakSet':
- case 'WeakMap':
- case 'Date':
- case 'Promise':
- case 'Error':
- return [node.typeName.name]
-
- // TS built-in utility types
- // https://www.typescriptlang.org/docs/handbook/utility-types.html
- case 'Partial':
- case 'Required':
- case 'Readonly':
- case 'Record':
- case 'Pick':
- case 'Omit':
- case 'InstanceType':
- return ['Object']
-
- case 'Uppercase':
- case 'Lowercase':
- case 'Capitalize':
- case 'Uncapitalize':
- return ['String']
-
- case 'Parameters':
- case 'ConstructorParameters':
- case 'ReadonlyArray':
- return ['Array']
-
- case 'ReadonlyMap':
- return ['Map']
- case 'ReadonlySet':
- return ['Set']
-
- case 'NonNullable':
- if (node.typeParameters && node.typeParameters.params[0]) {
- return inferRuntimeType(
- ctx,
- node.typeParameters.params[0],
- scope,
- ).filter(t => t !== 'null')
- }
- break
- case 'Extract':
- if (node.typeParameters && node.typeParameters.params[1]) {
- return inferRuntimeType(
- ctx,
- node.typeParameters.params[1],
- scope,
- )
- }
- break
- case 'Exclude':
- case 'OmitThisParameter':
- if (node.typeParameters && node.typeParameters.params[0]) {
- return inferRuntimeType(
- ctx,
- node.typeParameters.params[0],
- scope,
- )
- }
- break
+ case 'Parameters':
+ case 'ConstructorParameters':
+ case 'ReadonlyArray':
+ return ['Array']
+
+ case 'ReadonlyMap':
+ return ['Map']
+ case 'ReadonlySet':
+ return ['Set']
+
+ case 'NonNullable':
+ if (node.typeParameters && node.typeParameters.params[0]) {
+ return inferRuntimeType(
+ ctx,
+ node.typeParameters.params[0],
+ scope,
+ ).filter(t => t !== 'null')
+ }
+ break
+ case 'Extract':
+ if (node.typeParameters && node.typeParameters.params[1]) {
+ return inferRuntimeType(
+ ctx,
+ node.typeParameters.params[1],
+ scope,
+ )
+ }
+ break
+ case 'Exclude':
+ case 'OmitThisParameter':
+ if (node.typeParameters && node.typeParameters.params[0]) {
+ return inferRuntimeType(
+ ctx,
+ node.typeParameters.params[0],
+ scope,
+ )
+ }
+ break
+ }
}
}
// cannot infer, fallback to UNKNOWN: ThisParameterType
node.operator === 'keyof',
)
}
+
+ case 'TSAnyKeyword': {
+ if (isKeyOf) {
+ return ['String', 'Number', 'Symbol']
+ }
+ break
+ }
}
} catch (e) {
// always soft fail on failed runtime type inference