in src/assembler.ts [2139:2306]
private _optionalValue(type: ts.Type, declaration: ts.Node | undefined, purpose: TypeUseKind): spec.OptionalValue {
const isThisType = _isThisType(type, this._typeChecker, declaration?.parent);
if (type.isLiteral() && _isEnumLike(type)) {
type = this._typeChecker.getBaseTypeOfLiteralType(type);
} else {
type = this._typeChecker.getApparentType(type);
}
const primitiveType = _tryMakePrimitiveType.call(this);
if (primitiveType) {
return { type: primitiveType };
}
if (type.isUnion() && !_isEnumLike(type)) {
return _unionType.call(this);
}
if (!type.symbol) {
this._diagnostics.push(JsiiDiagnostic.JSII_1001_TYPE_HAS_NO_SYMBOL.create(declaration));
return { type: spec.CANONICAL_ANY };
}
if (type.symbol.name === 'Array') {
return { type: _arrayType.call(this) };
}
if (type.symbol.name === '__type' && type.symbol.members) {
return { type: _mapType.call(this) };
}
if (type.symbol.escapedName === 'Promise') {
const typeRef = type as ts.TypeReference;
if (!typeRef.typeArguments || typeRef.typeArguments.length !== 1) {
this._diagnostics.push(JsiiDiagnostic.JSII_1002_UNSPECIFIED_PROMISE.create(declaration));
return { type: spec.CANONICAL_ANY };
}
return {
type: this._typeReference(typeRef.typeArguments[0], declaration, purpose),
};
}
const fqn = this._getFQN(type, declaration, purpose, isThisType);
if (fqn == null) {
this._diagnostics.push(
JsiiDiagnostic.JSII_9997_UNKNOWN_ERROR.create(declaration, new Error('Could not determine FQN')),
);
return { type: { fqn: '' } };
}
return {
type: { fqn },
};
function _arrayType(this: Assembler): spec.CollectionTypeReference {
const typeRef = type as ts.TypeReference;
let elementtype: spec.TypeReference;
if (typeRef.typeArguments?.length === 1) {
elementtype = this._typeReference(typeRef.typeArguments[0], declaration, 'list element type');
} else {
const count = typeRef.typeArguments ? typeRef.typeArguments.length : 'none';
this._diagnostics.push(
JsiiDiagnostic.JSII_1003_UNSUPPORTED_TYPE.create(
declaration,
`Array references must have exactly one type argument (found ${count})`,
),
);
elementtype = spec.CANONICAL_ANY;
}
return {
collection: {
elementtype,
kind: spec.CollectionKind.Array,
},
};
}
function _mapType(this: Assembler): spec.CollectionTypeReference {
let elementtype: spec.TypeReference;
const objectType = type.getStringIndexType();
if (objectType) {
elementtype = this._typeReference(objectType, declaration, 'map element type');
} else {
const typeDecl = type.symbol.declarations?.[0];
if (
typeDecl != null &&
ts.isTypeLiteralNode(typeDecl) &&
typeDecl.members.length == 1 &&
ts.isIndexSignatureDeclaration(typeDecl.members[0]) &&
typeDecl.members[0].parameters[0].type != null &&
ts.isTemplateLiteralTypeNode(typeDecl.members[0].parameters[0].type)
) {
const indexTypeNode = typeDecl.members[0].type;
const indexType = this._typeChecker.getTypeFromTypeNode(indexTypeNode);
elementtype = this._typeReference(indexType, indexTypeNode, 'map element type');
} else {
this._diagnostics.push(
JsiiDiagnostic.JSII_1003_UNSUPPORTED_TYPE.create(
declaration,
'Only string-indexed map types are supported',
),
);
elementtype = spec.CANONICAL_ANY;
}
}
return {
collection: {
elementtype,
kind: spec.CollectionKind.Map,
},
};
}
function _tryMakePrimitiveType(this: Assembler): spec.PrimitiveTypeReference | undefined {
if (!type.symbol) {
if (type.flags & ts.TypeFlags.Object) {
if (isTupleType(type as ts.ObjectType)) {
this._diagnostics.push(
JsiiDiagnostic.JSII_1999_UNSUPPORTED.create(declaration, { what: 'Tuple types', alternative: 'arrays' }),
);
}
return { primitive: spec.PrimitiveType.Json };
}
if (type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) {
return spec.CANONICAL_ANY;
}
} else if (
type.symbol.valueDeclaration &&
isUnder(type.symbol.valueDeclaration.getSourceFile().fileName, this.stdlib)
) {
switch (type.symbol.name) {
case 'Boolean':
return { primitive: spec.PrimitiveType.Boolean };
case 'Date':
return { primitive: spec.PrimitiveType.Date };
case 'Number':
return { primitive: spec.PrimitiveType.Number };
case 'String':
return { primitive: spec.PrimitiveType.String };
}
}
// Not a primitive type!
return undefined;
}
function _unionType(this: Assembler): spec.OptionalValue {
const types = new Array<spec.TypeReference>();
let optional: boolean | undefined;
for (const subType of (type as ts.UnionType).types) {
if (subType.flags & ts.TypeFlags.Undefined) {
optional = true;
continue;
}
// eslint-disable-next-line no-await-in-loop
const resolvedType = this._typeReference(subType, declaration, purpose);
if (types.some((ref) => deepEqual(ref, resolvedType))) {
continue;
}
types.push(resolvedType);
}
return types.length === 1 ? { optional, type: types[0] } : { optional, type: { union: { types } } };
}
}