Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -2341,7 +2341,8 @@ Left.isOneOf(Keywords.kw_function, Keywords.kw_yield)) return false; if (Right.isOneOf(tok::l_brace, tok::l_square) && - Left.isOneOf(Keywords.kw_function, Keywords.kw_yield)) + Left.isOneOf(Keywords.kw_function, Keywords.kw_yield, + Keywords.kw_extends, Keywords.kw_implements)) return true; // JS methods can use some keywords as names (e.g. `delete()`). if (Right.is(tok::l_paren) && Line.MustBeDeclaration && Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -1970,6 +1970,17 @@ ((Style.Language == FormatStyle::LK_Java || Style.Language == FormatStyle::LK_JavaScript) && FormatTok->isOneOf(tok::period, tok::comma))) { + if (Style.Language == FormatStyle::LK_JavaScript && + FormatTok->isOneOf(Keywords.kw_extends, Keywords.kw_implements)) { + // JavaScript/TypeScript supports inline object types in + // extends/implements positions: + // class Foo implements {bar: number} { } + nextToken(); + if (FormatTok->is(tok::l_brace)) { + tryToParseBracedList(); + continue; + } + } bool IsNonMacroIdentifier = FormatTok->is(tok::identifier) && FormatTok->TokenText != FormatTok->TokenText.upper(); @@ -1989,7 +2000,7 @@ // and thus rule out the record production in case there is no template // (this would still leave us with an ambiguity between template function // and class declarations). - if (FormatTok->isOneOf(tok::colon, tok::less)) { + if (FormatTok->isOneOf(tok::colon, tok::less, Keywords.kw_extends)) { while (!eof()) { if (FormatTok->is(tok::l_brace)) { calculateBraceTypes(/*ExpectClassBody=*/true); Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -1400,6 +1400,17 @@ "}"); } +TEST_F(FormatTestJS, ObjectTypesInExtendsImplements) { + verifyFormat("class C extends {} {}"); + verifyFormat("class C implements {bar: number} {}"); + // Somewhat odd, but probably closest to reasonable formatting? + verifyFormat("class C implements {\n" + " bar: number,\n" + " baz: string,\n" + "} {}"); + verifyFormat("class C

{}"); +} + TEST_F(FormatTestJS, EnumDeclarations) { verifyFormat("enum Foo {\n" " A = 1,\n"