Skip to content

Commit

Permalink
fix: Pick up optional/readonly from mapped types
Browse files Browse the repository at this point in the history
Resolves #1509
  • Loading branch information
Gerrit0 committed Feb 20, 2021
1 parent e3aa04f commit a2d9dd8
Show file tree
Hide file tree
Showing 5 changed files with 855 additions and 482 deletions.
25 changes: 15 additions & 10 deletions src/lib/converter/symbols.ts
Expand Up @@ -324,7 +324,7 @@ function convertFunctionOrMethod(

if (declarations.length && isMethod) {
// All method signatures must have the same modifier flags.
setModifiers(declarations[0], reflection);
setModifiers(symbol, declarations[0], reflection);

assert(parentSymbol, "Tried to convert a method without a parent.");
if (
Expand Down Expand Up @@ -375,7 +375,7 @@ function convertClassOrInterface(
.getDeclarations()
?.find((d) => ts.isClassDeclaration(d) || ts.isFunctionDeclaration(d));
if (classDeclaration) {
setModifiers(classDeclaration, reflection);
setModifiers(symbol, classDeclaration, reflection);

// Classes can have static props
const staticType = context.checker.getTypeOfSymbolAtLocation(
Expand Down Expand Up @@ -421,7 +421,7 @@ function convertClassOrInterface(
.map((sig, i) => {
// Modifiers are the same for all constructors
if (sig.declaration && i === 0) {
setModifiers(sig.declaration, constructMember);
setModifiers(symbol, sig.declaration, constructMember);
}
const sigRef = createSignature(
constructContext,
Expand Down Expand Up @@ -574,7 +574,7 @@ function convertProperty(
ts.isParameter(declaration))
) {
parameterType = declaration.type;
setModifiers(declaration, reflection);
setModifiers(symbol, declaration, reflection);
const parentSymbol = context.project.getSymbolFromReflection(
context.scope
);
Expand Down Expand Up @@ -614,7 +614,7 @@ function convertArrowAsMethod(
symbol,
exportSymbol
);
setModifiers(arrow.parent as ts.PropertyDeclaration, reflection);
setModifiers(symbol, arrow.parent as ts.PropertyDeclaration, reflection);
const rc = context.withScope(reflection);

const signature = context.checker.getSignatureFromDeclaration(arrow);
Expand Down Expand Up @@ -771,7 +771,7 @@ function convertVariable(
typeNode ?? type
);

setModifiers(declaration, reflection);
setModifiers(symbol, declaration, reflection);

// Does anyone care about this? I doubt it...
if (
Expand Down Expand Up @@ -806,7 +806,7 @@ function convertVariableAsFunction(
symbol,
exportSymbol
);
setModifiers(declaration ?? symbol.valueDeclaration, reflection);
setModifiers(symbol, declaration ?? symbol.valueDeclaration, reflection);
// Does anyone care about this? I doubt it...
if (
declaration &&
Expand Down Expand Up @@ -892,7 +892,11 @@ function isInherited(context: Context, symbol: ts.Symbol) {
);
}

function setModifiers(declaration: ts.Declaration, reflection: Reflection) {
function setModifiers(
symbol: ts.Symbol,
declaration: ts.Declaration,
reflection: Reflection
) {
const modifiers = ts.getCombinedModifierFlags(declaration);
// Note: We only set this flag if the modifier is present because we allow
// fake "private" or "protected" members via @private and @protected
Expand All @@ -907,11 +911,12 @@ function setModifiers(declaration: ts.Declaration, reflection: Reflection) {
}
reflection.setFlag(
ReflectionFlag.Optional,
!!(declaration as any).questionToken
hasAllFlags(symbol.flags, ts.SymbolFlags.Optional)
);
reflection.setFlag(
ReflectionFlag.Readonly,
hasAllFlags(modifiers, ts.ModifierFlags.Readonly)
hasAllFlags(symbol.checkFlags ?? 0, ts.CheckFlags.Readonly) ||
hasAllFlags(modifiers, ts.ModifierFlags.Readonly)
);
reflection.setFlag(
ReflectionFlag.Abstract,
Expand Down
37 changes: 35 additions & 2 deletions src/lib/ts-internal.ts
Expand Up @@ -5,15 +5,48 @@ import * as ts from "typescript";
*/
declare module "typescript" {
interface Node {
// https://github.com/microsoft/TypeScript/blob/v4.1.3/src/compiler/types.ts#L847
// https://github.com/microsoft/TypeScript/blob/v4.1.5/src/compiler/types.ts#L847
symbol?: ts.Symbol;
}

interface Symbol {
// https://github.com/microsoft/TypeScript/blob/v4.1.5/src/compiler/types.ts#L4734-L4737
checkFlags?: CheckFlags;
}

interface TypeChecker {
// https://github.com/microsoft/TypeScript/blob/v4.1.3/src/compiler/types.ts#L4145
// https://github.com/microsoft/TypeScript/blob/v4.1.5/src/compiler/types.ts#L4145
// https://github.com/microsoft/TypeScript/issues/42118
getTypePredicateOfSignature(
signature: ts.Signature
): ts.TypePredicate | undefined;
}

// https://github.com/microsoft/TypeScript/blob/v4.1.5/src/compiler/types.ts#L4707-L4732
/* @internal */
export const enum CheckFlags {
Instantiated = 1 << 0, // Instantiated symbol
SyntheticProperty = 1 << 1, // Property in union or intersection type
SyntheticMethod = 1 << 2, // Method in union or intersection type
Readonly = 1 << 3, // Readonly transient symbol
ReadPartial = 1 << 4, // Synthetic property present in some but not all constituents
WritePartial = 1 << 5, // Synthetic property present in some but only satisfied by an index signature in others
HasNonUniformType = 1 << 6, // Synthetic property with non-uniform type in constituents
HasLiteralType = 1 << 7, // Synthetic property with at least one literal type in constituents
ContainsPublic = 1 << 8, // Synthetic property with public constituent(s)
ContainsProtected = 1 << 9, // Synthetic property with protected constituent(s)
ContainsPrivate = 1 << 10, // Synthetic property with private constituent(s)
ContainsStatic = 1 << 11, // Synthetic property with static constituent(s)
Late = 1 << 12, // Late-bound symbol for a computed property with a dynamic name
ReverseMapped = 1 << 13, // Property of reverse-inferred homomorphic mapped type
OptionalParameter = 1 << 14, // Optional parameter
RestParameter = 1 << 15, // Rest parameter
DeferredType = 1 << 16, // Calculation of the type of this symbol is deferred due to processing costs, should be fetched with `getTypeOfSymbolWithDeferredType`
HasNeverType = 1 << 17, // Synthetic property with at least one never type in constituents
Mapped = 1 << 18, // Property of mapped type
StripOptional = 1 << 19, // Strip optionality in mapped property
Synthetic = SyntheticProperty | SyntheticMethod,
Discriminant = HasNonUniformType | HasLiteralType,
Partial = ReadPartial | WritePartial,
}
}
9 changes: 9 additions & 0 deletions src/test/converter/class/class.ts
Expand Up @@ -121,3 +121,12 @@ export class Ts38PrivateFields {
/** Docs */
#foo = 1;
}

export namespace GH1509 {
export interface Foo {
foo: number;
}

export interface PartialFoo extends Partial<Foo> {}
export interface ReadonlyFoo extends Readonly<Partial<Foo>> {}
}

0 comments on commit a2d9dd8

Please sign in to comment.