diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 75be36474222f..0c7dbe1fd0c54 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -42624,6 +42624,48 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return undefined;
}
+ function getSignaturesFromCallLike(node: CallLikeExpression): readonly Signature[] {
+ switch (node.kind) {
+ case SyntaxKind.CallExpression:
+ case SyntaxKind.Decorator:
+ return getSignaturesOfType(
+ getTypeOfExpression(node.expression),
+ SignatureKind.Call,
+ );
+ case SyntaxKind.NewExpression:
+ return getSignaturesOfType(
+ getTypeOfExpression(node.expression),
+ SignatureKind.Construct,
+ );
+ case SyntaxKind.JsxSelfClosingElement:
+ case SyntaxKind.JsxOpeningElement:
+ if (isJsxIntrinsicTagName(node.tagName)) return [];
+ return getSignaturesOfType(
+ getTypeOfExpression(node.tagName),
+ SignatureKind.Call,
+ );
+ case SyntaxKind.TaggedTemplateExpression:
+ return getSignaturesOfType(
+ getTypeOfExpression(node.tag),
+ SignatureKind.Call,
+ );
+ case SyntaxKind.BinaryExpression:
+ case SyntaxKind.JsxOpeningFragment:
+ return [];
+ }
+ }
+
+ function getTypeParameterConstraintForPositionAcrossSignatures(signatures: readonly Signature[], position: number) {
+ const relevantTypeParameterConstraints = flatMap(signatures, signature => {
+ const relevantTypeParameter = signature.typeParameters?.[position];
+ if (relevantTypeParameter === undefined) return [];
+ const relevantConstraint = getConstraintOfTypeParameter(relevantTypeParameter);
+ if (relevantConstraint === undefined) return [];
+ return [relevantConstraint];
+ });
+ return getUnionType(relevantTypeParameterConstraints);
+ }
+
function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) {
checkGrammarTypeArguments(node, node.typeArguments);
if (node.kind === SyntaxKind.TypeReference && !isInJSFile(node) && !isInJSDoc(node) && node.typeArguments && node.typeName.end !== node.typeArguments.pos) {
@@ -42662,12 +42704,62 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function getTypeArgumentConstraint(node: TypeNode): Type | undefined {
- const typeReferenceNode = tryCast(node.parent, isTypeReferenceType);
- if (!typeReferenceNode) return undefined;
- const typeParameters = getTypeParametersForTypeReferenceOrImport(typeReferenceNode);
- if (!typeParameters) return undefined;
- const constraint = getConstraintOfTypeParameter(typeParameters[typeReferenceNode.typeArguments!.indexOf(node)]);
- return constraint && instantiateType(constraint, createTypeMapper(typeParameters, getEffectiveTypeArguments(typeReferenceNode, typeParameters)));
+ let typeArgumentPosition;
+ if (
+ "typeArguments" in node.parent && // eslint-disable-line local/no-in-operator
+ Array.isArray(node.parent.typeArguments)
+ ) {
+ typeArgumentPosition = node.parent.typeArguments.indexOf(node);
+ }
+
+ if (typeArgumentPosition !== undefined) {
+ // The node could be a type argument of a call, a `new` expression, a decorator, an
+ // instantiation expression, or a generic type instantiation.
+
+ if (isCallLikeExpression(node.parent)) {
+ return getTypeParameterConstraintForPositionAcrossSignatures(
+ getSignaturesFromCallLike(node.parent),
+ typeArgumentPosition,
+ );
+ }
+
+ if (isDecorator(node.parent.parent)) {
+ return getTypeParameterConstraintForPositionAcrossSignatures(
+ getSignaturesFromCallLike(node.parent.parent),
+ typeArgumentPosition,
+ );
+ }
+
+ if (isExpressionWithTypeArguments(node.parent) && isExpressionStatement(node.parent.parent)) {
+ const uninstantiatedType = checkExpression(node.parent.expression);
+
+ const callConstraint = getTypeParameterConstraintForPositionAcrossSignatures(
+ getSignaturesOfType(uninstantiatedType, SignatureKind.Call),
+ typeArgumentPosition,
+ );
+ const constructConstraint = getTypeParameterConstraintForPositionAcrossSignatures(
+ getSignaturesOfType(uninstantiatedType, SignatureKind.Construct),
+ typeArgumentPosition,
+ );
+
+ // An instantiation expression instantiates both call and construct signatures, so
+ // if both exist type arguments must be assignable to both constraints.
+ if (constructConstraint.flags & TypeFlags.Never) return callConstraint;
+ if (callConstraint.flags & TypeFlags.Never) return constructConstraint;
+ return getIntersectionType([callConstraint, constructConstraint]);
+ }
+
+ if (isTypeReferenceType(node.parent)) {
+ const typeParameters = getTypeParametersForTypeReferenceOrImport(node.parent);
+ if (!typeParameters) return undefined;
+ const relevantTypeParameter = typeParameters[typeArgumentPosition];
+ const constraint = getConstraintOfTypeParameter(relevantTypeParameter);
+ return constraint && instantiateType(
+ constraint,
+ createTypeMapper(typeParameters, getEffectiveTypeArguments(node.parent, typeParameters)),
+ );
+ }
+ }
}
function checkTypeQuery(node: TypeQueryNode) {
diff --git a/src/services/completions.ts b/src/services/completions.ts
index dc01ea8ede4b9..f9e99d5171c60 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -256,7 +256,6 @@ import {
isTypeOnlyImportDeclaration,
isTypeOnlyImportOrExportDeclaration,
isTypeParameterDeclaration,
- isTypeReferenceType,
isValidTypeOnlyAliasUseSite,
isVariableDeclaration,
isVariableLike,
@@ -3626,17 +3625,20 @@ function getCompletionData(
}
log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));
- const contextualType = previousToken && getContextualType(previousToken, position, sourceFile, typeChecker);
+ const contextualTypeOrConstraint = previousToken && (
+ getContextualType(previousToken, position, sourceFile, typeChecker) ??
+ getConstraintOfTypeArgumentProperty(previousToken, typeChecker)
+ );
// exclude literal suggestions after (#51667) and after closing quote (#52675)
// for strings getStringLiteralCompletions handles completions
const isLiteralExpected = !tryCast(previousToken, isStringLiteralLike) && !isJsxIdentifierExpected;
const literals = !isLiteralExpected ? [] : mapDefined(
- contextualType && (contextualType.isUnion() ? contextualType.types : [contextualType]),
+ contextualTypeOrConstraint && (contextualTypeOrConstraint.isUnion() ? contextualTypeOrConstraint.types : [contextualTypeOrConstraint]),
t => t.isLiteral() && !(t.flags & TypeFlags.EnumLiteral) ? t.value : undefined,
);
- const recommendedCompletion = previousToken && contextualType && getRecommendedCompletion(previousToken, contextualType, typeChecker);
+ const recommendedCompletion = previousToken && contextualTypeOrConstraint && getRecommendedCompletion(previousToken, contextualTypeOrConstraint, typeChecker);
return {
kind: CompletionDataKind.Data,
symbols,
@@ -5766,11 +5768,13 @@ function tryGetTypeLiteralNode(node: Node): TypeLiteralNode | undefined {
return undefined;
}
-function getConstraintOfTypeArgumentProperty(node: Node, checker: TypeChecker): Type | undefined {
+/** @internal */
+export function getConstraintOfTypeArgumentProperty(node: Node, checker: TypeChecker): Type | undefined {
if (!node) return undefined;
- if (isTypeNode(node) && isTypeReferenceType(node.parent)) {
- return checker.getTypeArgumentConstraint(node);
+ if (isTypeNode(node)) {
+ const constraint = checker.getTypeArgumentConstraint(node);
+ if (constraint) return constraint;
}
const t = getConstraintOfTypeArgumentProperty(node.parent, checker);
@@ -5779,10 +5783,19 @@ function getConstraintOfTypeArgumentProperty(node: Node, checker: TypeChecker):
switch (node.kind) {
case SyntaxKind.PropertySignature:
return checker.getTypeOfPropertyOfContextualType(t, (node as PropertySignature).symbol.escapedName);
+ case SyntaxKind.ColonToken:
+ if (node.parent.kind === SyntaxKind.PropertySignature) {
+ // The cursor is at a property value location like `Foo<{ x: | }`.
+ // `t` already refers to the appropriate property type.
+ return t;
+ }
+ break;
case SyntaxKind.IntersectionType:
case SyntaxKind.TypeLiteral:
case SyntaxKind.UnionType:
return t;
+ case SyntaxKind.OpenBracketToken:
+ return checker.getElementTypeOfArrayType(t);
}
}
diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts
index 6c5523ae1953c..5ce81d089992e 100644
--- a/src/services/stringCompletions.ts
+++ b/src/services/stringCompletions.ts
@@ -3,6 +3,7 @@ import {
createCompletionDetails,
createCompletionDetailsForSymbol,
getCompletionEntriesFromSymbols,
+ getConstraintOfTypeArgumentProperty,
getDefaultCommitCharacters,
getPropertiesForObjectExpression,
Log,
@@ -509,7 +510,12 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
function fromUnionableLiteralType(grandParent: Node): StringLiteralCompletionsFromTypes | StringLiteralCompletionsFromProperties | undefined {
switch (grandParent.kind) {
+ case SyntaxKind.CallExpression:
case SyntaxKind.ExpressionWithTypeArguments:
+ case SyntaxKind.JsxOpeningElement:
+ case SyntaxKind.JsxSelfClosingElement:
+ case SyntaxKind.NewExpression:
+ case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.TypeReference: {
const typeArgument = findAncestor(parent, n => n.parent === grandParent) as LiteralTypeNode;
if (typeArgument) {
@@ -529,6 +535,8 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
return undefined;
}
return stringLiteralCompletionsFromProperties(typeChecker.getTypeFromTypeNode(objectType));
+ case SyntaxKind.PropertySignature:
+ return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(getConstraintOfTypeArgumentProperty(grandParent, typeChecker)), isNewIdentifier: false };
case SyntaxKind.UnionType: {
const result = fromUnionableLiteralType(walkUpParentheses(grandParent.parent));
if (!result) {
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter10.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter10.ts
new file mode 100644
index 0000000000000..892e88a882558
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter10.ts
@@ -0,0 +1,38 @@
+///
+
+////interface Foo {
+//// one: string;
+//// two: number;
+////}
+////interface Bar {
+//// three: boolean;
+//// four: {
+//// five: unknown;
+//// };
+////}
+////
+////function a() {}
+////a<{/*0*/}>();
+////
+////var b = () => () => {};
+////b()<{/*1*/}>();
+////
+////declare function c(): void
+////declare function c(): void
+////c<{/*2*/}>();
+////
+////function d() {}
+////d<{/*3*/}, {/*4*/}>();
+////d();
+////
+////(() => {})<{/*6*/}>();
+
+verify.completions(
+ { marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+ { marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+ { marker: "2", unsorted: ["one", "two", "three", "four"], isNewIdentifierLocation: true },
+ { marker: "3", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+ { marker: "4", unsorted: ["three", "four"], isNewIdentifierLocation: true },
+ { marker: "5", unsorted: ["five"], isNewIdentifierLocation: true },
+ { marker: "6", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+);
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter11.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter11.ts
new file mode 100644
index 0000000000000..634746dd38582
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter11.ts
@@ -0,0 +1,32 @@
+///
+
+////interface Foo {
+//// one: string;
+//// two: number;
+////}
+////interface Bar {
+//// three: boolean;
+//// four: symbol;
+////}
+////
+////class A {}
+////new A<{/*0*/}>();
+////
+////class B {}
+////new B<{/*1*/}, {/*2*/}>();
+////
+////declare const C: {
+//// new (): unknown
+//// new (): unknown
+////}
+////new C<{/*3*/}>()
+////
+////new (class {})<{/*4*/}>();
+
+verify.completions(
+ { marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+ { marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+ { marker: "2", unsorted: ["three", "four"], isNewIdentifierLocation: true },
+ { marker: "3", unsorted: ["one", "two", "three", "four"], isNewIdentifierLocation: true },
+ { marker: "4", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+);
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter12.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter12.ts
new file mode 100644
index 0000000000000..9bf8ec21afaae
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter12.ts
@@ -0,0 +1,25 @@
+///
+
+////interface Foo {
+//// kind: 'foo';
+//// one: string;
+////}
+////interface Bar {
+//// kind: 'bar';
+//// two: number;
+////}
+////
+////declare function a(): void
+////declare function a(): void
+////a<{ kind: 'bar', /*0*/ }>();
+////
+////declare function b(kind: 'foo'): void
+////declare function b(kind: 'bar'): void
+////b<{/*1*/}>('bar');
+
+// The completion lists are unfortunately not narrowed here (ideally only
+// properties of `Bar` would be suggested).
+verify.completions(
+ { marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+ { marker: "1", unsorted: ["kind", "one", "two"], isNewIdentifierLocation: true },
+);
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter13.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter13.ts
new file mode 100644
index 0000000000000..5797134b2b15e
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter13.ts
@@ -0,0 +1,18 @@
+///
+
+// @jsx: preserve
+// @filename: a.tsx
+////interface Foo {
+//// one: string;
+//// two: number;
+////}
+////
+////const Component = () => <>>;
+////
+////>;
+/////>;
+
+verify.completions(
+ { marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+ { marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+);
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter14.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter14.ts
new file mode 100644
index 0000000000000..79aa45875715e
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter14.ts
@@ -0,0 +1,10 @@
+///
+
+////interface Foo {
+//// one: string;
+//// two: number;
+////}
+////declare function f(x: TemplateStringsArray): void;
+////f<{/*0*/}>``;
+
+verify.completions({ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true });
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter15.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter15.ts
new file mode 100644
index 0000000000000..352facb90a46d
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter15.ts
@@ -0,0 +1,15 @@
+///
+
+////interface Foo {
+//// one: string;
+//// two: number;
+////}
+////
+////declare function decorator(originalMethod: unknown, _context: unknown): never
+////
+////class {
+//// @decorator<{/*0*/}>
+//// method() {}
+////}
+
+verify.completions({ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true });
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter16.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter16.ts
new file mode 100644
index 0000000000000..4c32c2d19fc22
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter16.ts
@@ -0,0 +1,35 @@
+///
+
+////interface Foo {
+//// one: string;
+//// two: number;
+////}
+////interface Bar {
+//// three: boolean;
+//// four: {
+//// five: unknown;
+//// };
+////}
+////
+////(() => {})<{/*0*/}>;
+////
+////(class {})<{/*1*/}>;
+////
+////declare const a: {
+//// new (): {};
+//// (): {};
+////}
+////a<{/*2*/}>;
+////
+////declare const b: {
+//// new (): {};
+//// (): {};
+////}
+////b<{/*3*/}>;
+
+verify.completions(
+ { marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+ { marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: true },
+ { marker: "2", unsorted: ["one", "two", "three", "four"], isNewIdentifierLocation: true },
+ { marker: "3", unsorted: [], isNewIdentifierLocation: true },
+);
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter17.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter17.ts
new file mode 100644
index 0000000000000..87e0d33dbb3b0
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter17.ts
@@ -0,0 +1,18 @@
+///
+
+////class Foo {}
+////function foo() {}
+////
+////type A = Foo<{ x: /*0*/ }>;
+////new Foo<{ x: /*1*/ }>();
+////foo<{ x: /*2*/ }>();
+////foo<{ x: /*3*/ }>;
+////Foo<{ x: /*4*/ }>;
+
+verify.completions(
+ { marker: "0", includes: ['"one"', '2'], isNewIdentifierLocation: false },
+ { marker: "1", includes: ['"one"', '2'], isNewIdentifierLocation: false },
+ { marker: "2", includes: ['"one"', '2'], isNewIdentifierLocation: false },
+ { marker: "3", includes: ['"one"', '2'], isNewIdentifierLocation: false },
+ { marker: "4", includes: ['"one"', '2'], isNewIdentifierLocation: false },
+);
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter18.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter18.ts
new file mode 100644
index 0000000000000..0d3c8e7b5a474
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter18.ts
@@ -0,0 +1,24 @@
+///
+
+////class Foo {}
+////function foo() {}
+////declare function tag(x: TemplateStringsArray): void;
+////declare function decorator(...args: unknown[]): never
+////
+////type A = Foo<{ x: '/*0*/' }>;
+////new Foo<{ x: '/*1*/' }>();
+////foo<{ x: '/*2*/' }>();
+////foo<{ x: '/*3*/' }>;
+////Foo<{ x: '/*4*/' }>;
+////tag<{ x: '/*5*/' }>``;
+////class { @decorator<{ x: '/*6*/' }>; method() {} }
+
+verify.completions(
+ { marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "2", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "3", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "4", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "5", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "6", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+);
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter19.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter19.ts
new file mode 100644
index 0000000000000..96b8348dbaf8a
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter19.ts
@@ -0,0 +1,24 @@
+///
+
+////class Foo {}
+////function foo() {}
+////declare function tag(x: TemplateStringsArray): void;
+////declare function decorator(...args: unknown[]): never
+////
+////type A = Foo<'/*0*/'>;
+////new Foo<'/*1*/'>();
+////foo<'/*2*/'>();
+////foo<'/*3*/'>;
+////Foo<'/*4*/'>;
+////tag<'/*5*/'>``;
+////class { @decorator<'/*6*/'>; method() {} }
+
+verify.completions(
+ { marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "2", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "3", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "4", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "5", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "6", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+);
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter20.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter20.ts
new file mode 100644
index 0000000000000..d2649be7f95ab
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter20.ts
@@ -0,0 +1,18 @@
+///
+
+// @jsx: preserve
+// @filename: a.tsx
+////const Component1 = () => <>>;
+////const Component2 = () => <>>;
+////
+////>;
+/////>;
+////>;
+/////>;
+
+verify.completions(
+ { marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "2", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+ { marker: "3", unsorted: ["one", "two"], isNewIdentifierLocation: false },
+);
diff --git a/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter21.ts b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter21.ts
new file mode 100644
index 0000000000000..b2e6a6cea4b2a
--- /dev/null
+++ b/tests/cases/fourslash/completionListInTypeLiteralInTypeParameter21.ts
@@ -0,0 +1,18 @@
+///
+
+////class Foo {}
+////function foo() {}
+////
+////type A = Foo<[/*0*/]>;
+////new Foo<[/*1*/]>();
+////foo<[/*2*/]>();
+////foo<[/*3*/]>;
+////Foo<[/*4*/]>;
+
+verify.completions(
+ { marker: "0", includes: ['"one"', '2'], isNewIdentifierLocation: true },
+ { marker: "1", includes: ['"one"', '2'], isNewIdentifierLocation: true },
+ { marker: "2", includes: ['"one"', '2'], isNewIdentifierLocation: true },
+ { marker: "3", includes: ['"one"', '2'], isNewIdentifierLocation: true },
+ { marker: "4", includes: ['"one"', '2'], isNewIdentifierLocation: true }
+);