literalUnionMixed: { type: [String, Number, Boolean], required: true },
intersection: { type: Object, required: true },
intersection2: { type: String, required: true },
- foo: { type: [Function, null], required: true }
+ foo: { type: [Function, null], required: true },
+ unknown: { type: null, required: true },
+ unknownUnion: { type: null, required: true },
+ unknownIntersection: { type: Object, required: true }
},
setup(__props: any, { expose }) {
expose();
intersection: Test & {}
intersection2: 'foo' & ('foo' | 'bar')
foo: ((item: any) => boolean) | null
+
+ unknown: UnknownType
+ unknownUnion: UnknownType | string
+ unknownIntersection: UnknownType & Object
}>()
</script>`)
assertCode(content)
expect(content).toMatch(`intersection: { type: Object, required: true }`)
expect(content).toMatch(`intersection2: { type: String, required: true }`)
expect(content).toMatch(`foo: { type: [Function, null], required: true }`)
+ expect(content).toMatch(`unknown: { type: null, required: true }`)
+ // uninon containing unknown type: skip check
+ expect(content).toMatch(`unknownUnion: { type: null, required: true }`)
+ // intersection containing unknown type: narrow to the known types
+ expect(content).toMatch(
+ `unknownIntersection: { type: Object, required: true }`
+ )
expect(bindings).toStrictEqual({
string: BindingTypes.PROPS,
number: BindingTypes.PROPS,
foo: BindingTypes.PROPS,
uppercase: BindingTypes.PROPS,
params: BindingTypes.PROPS,
- nonNull: BindingTypes.PROPS
+ nonNull: BindingTypes.PROPS,
+ unknown: BindingTypes.PROPS,
+ unknownUnion: BindingTypes.PROPS,
+ unknownIntersection: BindingTypes.PROPS
})
})
isFromSetup: boolean,
needTemplateUsageCheck: boolean
) {
- // template usage check is only needed in non-inline mode, so we can skip
+ // template usage check is only needed in non-inline mode, so we can UNKNOWN
// the work if inlineTemplate is true.
let isUsedInTemplate = needTemplateUsageCheck
if (
// check if user has manually specified `name` or 'render` option in
// export default
- // if has name, skip name inference
+ // if has name, UNKNOWN name inference
// if has render and no template, generate return object instead of
// empty render function (#4980)
let optionProperties
// 4. extract runtime props/emits code from setup context type
if (propsTypeDecl) {
- extractRuntimeProps(propsTypeDecl, typeDeclaredProps, declaredTypes, isProd)
+ extractRuntimeProps(propsTypeDecl, typeDeclaredProps, declaredTypes)
}
if (emitsTypeDecl) {
extractRuntimeEmits(emitsTypeDecl, typeDeclaredEmits)
!userImports[key].source.endsWith('.vue')
) {
// generate getter for import bindings
- // skip vue imports since we know they will never change
+ // UNKNOWN vue imports since we know they will never change
returned += `get ${key}() { return ${key} }, `
} else if (bindingMetadata[key] === BindingTypes.SETUP_LET) {
// local let binding, also add setter
function extractRuntimeProps(
node: TSTypeLiteral | TSInterfaceBody,
props: Record<string, PropTypeData>,
- declaredTypes: Record<string, string[]>,
- isProd: boolean
+ declaredTypes: Record<string, string[]>
) {
const members = node.type === 'TSTypeLiteral' ? node.members : node.body
for (const m of members) {
(m.type === 'TSPropertySignature' || m.type === 'TSMethodSignature') &&
m.key.type === 'Identifier'
) {
- let type
+ let type: string[] | undefined
if (m.type === 'TSMethodSignature') {
type = ['Function']
} else if (m.typeAnnotation) {
type = inferRuntimeType(m.typeAnnotation.typeAnnotation, declaredTypes)
+ // skip check for result containing unknown types
+ if (type.includes(UNKNOWN_TYPE)) {
+ type = [`null`]
+ }
}
props[m.key.name] = {
key: m.key.name,
}
}
+const UNKNOWN_TYPE = 'Unknown'
+
function inferRuntimeType(
node: TSType,
declaredTypes: Record<string, string[]>
return ['Boolean']
case 'TSObjectKeyword':
return ['Object']
+ case 'TSNullKeyword':
+ return ['null']
case 'TSTypeLiteral': {
// TODO (nice to have) generate runtime property validation
const types = new Set<string>()
case 'BigIntLiteral':
return ['Number']
default:
- return [`null`]
+ return [`UNKNOWN`]
}
case 'TSTypeReference':
declaredTypes
)
}
- // cannot infer, fallback to null: ThisParameterType
+ // cannot infer, fallback to UNKNOWN: ThisParameterType
}
}
- return [`null`]
+ return [UNKNOWN_TYPE]
case 'TSParenthesizedType':
return inferRuntimeType(node.typeAnnotation, declaredTypes)
+
case 'TSUnionType':
- case 'TSIntersectionType':
- return [
- ...new Set(
- [].concat(
- ...(node.types.map(t => inferRuntimeType(t, declaredTypes)) as any)
- )
- )
- ]
+ return flattenTypes(node.types, declaredTypes)
+ case 'TSIntersectionType': {
+ return flattenTypes(node.types, declaredTypes).filter(
+ t => t !== UNKNOWN_TYPE
+ )
+ }
case 'TSSymbolKeyword':
return ['Symbol']
default:
- return [`null`] // no runtime check
+ return [UNKNOWN_TYPE] // no runtime check
}
}
+function flattenTypes(
+ types: TSType[],
+ declaredTypes: Record<string, string[]>
+): string[] {
+ return [
+ ...new Set(
+ ([] as string[]).concat(
+ ...types.map(t => inferRuntimeType(t, declaredTypes))
+ )
+ )
+ ]
+}
+
function toRuntimeTypeString(types: string[]) {
return types.length > 1 ? `[${types.join(', ')}]` : types[0]
}