Repository: prettier/prettier (JavaScript)

Issue:
Prettier removes parenthesis around Typescript instantiation expression that is used as part of a bind call
**Prettier 2.8.7**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuc0DOMAEBbAngeQCMArOMLAXk2AB0pNMBBJTARgBo6GAhFgJk71MAYRYBmOgF9MAQzSZIUDAG46dGLgAOcTAFkCJMjABqMgDYBXOABUtOqgAoN2iADMcB0uQCUAbQDWcLhumM5wIXhEXjAAuqpQdK4WUOQAltCYqWimZqkAJjlWADzGmHAAHvBQefJQFtiEcABOAHwOXJgQJCwASmQQTXlFGE2pUADm7JjGLVMdAG7mVix1Dc103iyLljpZ09QdTXAwFk30UUYAdNtWaA5dxN6XY2CWeXB3Nzpy097xkmooEkUjB0vRxsccvlCnASmVKggaphVo1Wu0hA9ev1BsMYKMJlMZoIGF8WCMxuNMAAfZH1VHU2lmMwM5LvVxjOB5DYsUo01lwdlQTkHISpdwOL6YCjSxnMqk0yXSqj8wWc7wihgMI4nM6YFUcrlCaRwMxoHS0IQMRQYWlrJowqWYABydOaEqWcD+HS1x1O9CyUIKHvuJCmKOaMPVAH5baiHSx9UK8vEGACoGm6NasBCTOZ8voLuQHY4c4GYUUC4Yix7bNoWk9CGM8g46kyppEqzA-iB2CAIJpQehkKAZE0mhAAO4ABVHCDQyBA5gnMlw897hCaMjAgRgAGUZNg4AAZDnIVzmM3rzfb467zRbinIPFWXtwNZ5d55I8yCYWGQQgAxAZsBkGBQQmBcZAsGAIB7EAAAsYGwMwAHV4NSeA0HvMA4F3OcMNSeYMNwBcwDQNcQDGM0mhgKdN3GECzwvOBe2INByl3CkzDgABFCwIHgJjTRYkB7yaaiFzCNAwFGAc4M0fEYBQ-IYHg5AAA4AAZewUiAzRQzdNAXBSPmaeY4DggBHfj4Do-t5xQOQAFohU5Tk4KOazUiOOj-0YpBz2E3szWwVInyaF8QDQLi4EYMDRkIaC4CnZoTyFITLyimK+IEiyAuY3sYBkQhlLyVTkAEEA8RkVJcgmYQIGwfyQA+ABWOCLDNaxiocwLMvmKwAElqgQPcZNSAdGGqXcNG4jK4EkSQgA)
<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**
<!-- prettier-ignore -->
```tsx
const myObject = {
  A: 1,
  B: 2,
  C: 3
} as const;

type MyObjectValueType = (typeof myObject)[keyof typeof myObject];

function isValidValue<V extends number>(
  obj: Record<string, V>, 
  value: number
): value is V {
  return Object.values(obj).includes(value as V);
}

function getValidValue<V extends number>(
  obj: Record<string, V>,
  value: string | number | null | undefined
): V | undefined {
  if (value === null || value === undefined) {
    return undefined
  } else {
    const numberValue = Number(value);
    return isValidValue(obj, numberValue) ? numberValue : undefined;
  }
}

const getValidMyObjectValue = (getValidValue<MyObjectValueType>).bind(null, myObject);
```

**Output:**
<!-- prettier-ignore -->
```tsx
const myObject = {
  A: 1,
  B: 2,
  C: 3,
} as const;

type MyObjectValueType = (typeof myObject)[keyof typeof myObject];

function isValidValue<V extends number>(
  obj: Record<string, V>,
  value: number
): value is V {
  return Object.values(obj).includes(value as V);
}

function getValidValue<V extends number>(
  obj: Record<string, V>,
  value: string | number | null | undefined
): V | undefined {
  if (value === null || value === undefined) {
    return undefined;
  } else {
    const numberValue = Number(value);
    return isValidValue(obj, numberValue) ? numberValue : undefined;
  }
}

const getValidMyObjectValue = getValidValue<MyObjectValueType>.bind(
  null,
  myObject
);

```

**Expected behavior:**
The parenthesis should not be removed from the Typescript instantiation expression.
```tsx
const getValidMyObjectValue = (getValidValue<MyObjectValueType>).bind(
  null,
  myObject
);
```
If they are, it results in the following Typescript error
```sh
TS1477: An instantiation expression cannot be followed by a property access.
```

[Typescript playground for unformatted code](https://www.typescriptlang.org/play?#code/MYewdgzgLgBAtgTwPICMBWBTYsC8MDeAUDDAIIBcMAjADTEwBClATHSQMKUDMhAvjAEMIMUJCgBuQoSgIADhhgBZZOixQAagIA2AVwwAVOQrwAKGfJAAzeCszYAlAG0A1hgRWY5jB8So7UAF1JQksdMGwAS3AYCIhNLQiAE3i9AB51GAwADygMMEThMB04FAwAJwA+E3oQdEoAJSwQMsTU6DKIsABzGhh1Ct76ADdtPUoikvLCe0oR3QVYvoJ6MowoHTKwGD81ADo5vQgTWrR7Xc7gXUSMI4OFIT77SV4pUPCoKK2utfiklIx0pkcnkCjAJqVKtUSCcGk0Wm0oB1ur1+mwYHdKO1Ol0YAAfMHFCF4glaLTEsLXSydDCJaaUDL4ikYKlgGnLEgRawmO4wHB8klk3H4nl8vBMlk0+zskgwVbrTYwcXU2kkfgYLQQBREGUicDQAmTMr-XkwAByhPK3NGGCe9BIco2W1iv2S1uO6F64PK-ylAH4DRDjZQlazEpJVXwpKJ9d8NNoksodthjaZYy7-qlE6pk9bDPIKmcUJ1EiYiqTer5s1AnkA)

[Typescript playground for formatted code](https://www.typescriptlang.org/play?#code/MYewdgzgLgBAtgTwPICMBWBTYsC8MDeAUDDAIIBcMAjADTEwBClATHSQMKUDMdAvjAEMIMUJCgBuQoSgIADhhgBZZOixQAagIA2AVwwAVOQrwAKGfJAAzeCszYAlAG0A1hgRWY5jB8So7UAF1JQksdMGwAS3AYCIhNLQiAE3i9AB51GAwADygMMEThMB04FAwAJwA+E3oQdEoAJSwQMsTU6DKIsABzGhh1CrYYADdtPUoikvLCe0oR3QVYvoJ6MowoHTKwGD81ADo5vQgTWrR7Xc7gXUSMI4OFIT77SV4pUPCoKK2utfiklIx0pkcnkCjAJqVKtUSCcGk0Wm0oB1ur1+oM7pR2p0ujAAD5g4oQ3H4rRaIlha6WToYRLTSgZPHkjCUsDU5YkCLWEx3GA4XnE0k4vHc3l4RnM6n2NkkGCrdabGBiqmJSQkfgYLQQBREaUicDQfGTMr-HkwAByBPKXNGGCe9BIso2W1iv2S1uO6F64PK-0lAH4DRDjZRFSzlfQXi9CKJ9d8NNoksodthjXhYy7-qlE6pk9bDPIKrsUJ1ElD+YNfNmoNNxEA)


Hints:
👋 @christopherschroer! Which version of TypeScript are you using? I just tried pasting

```ts
const getValidMyObjectValue = getValidValue<MyObjectValueType>.bind(
  null,
  myObject
);
```
into TS playground and [it successfully generated JavaScript](https://www.typescriptlang.org/play?ts=5.0.2#code/MYewdgzgLgBA5gUygNQIYBsCWATAsgTwHkAjAKwWBQwFcEYBeeJNLbF2gHgJPMvYQAq+AA4IAfADpimMNgAUAKBgww1dOgA0SmAFsiZClAUBKANwKgA) for both v5.0.2 and 4.9.5.
Apologies @kachkaev. There was an error in the pasted the code above. I have edited the original post/playground link. It should properly reflect the issue. I have also included links in the original post to typescript playground so you can see the difference

As for which version of Typescript I am using, it is 5.0.2
I see, thanks! I’ve tried co come up with a shorter example and I _guess_ have been able to reproduce your issue:

**Prettier 2.8.7**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AEA5nDABJwA2FEAPAMoQC2cAKgJ4AOc+cAHvFAAmAZ3zB8AC0rUk+YTABOOKIXwBfAHwAKADpR8+GBJzDZDZuy56AlGaatOcANo6QUqhFcBdMXoMKSdAV9IxMAOndqAG49NT09SCh5OS4wHFQcMAB5ACMAKzhsfABeX31JaQhZVwB3CAUKQVcAGj98VAgq-FccgEMFFtj8XtFE+RioPRhHfDpU9MzLblLprghUFMKF7PzCmAmE6GTiGAAhCExBcg8S-C0T6+p6eYywJY1rMJzlQS1hF8yuQK2GsURAzRAEA4uCOyFA-QUEBqAAV+ghhMgQL0KDVemwMRCcgpemAANYkOi9ZgAGWUcGQqGxwjghOJZIpHBJykIyEU6BZIDgjBycEEglF1N6KnQvWIADF6oxejBcCpMb10DAIOC3DBGBQAOrGeD-ElwOjonC4ABuVrYmLAwgJIGUzIUMGRxMISoZTIFeWEvDo3IocAAiugIPBfRRmRDOQo3ZjVnBhGAlNCdRwlLADThBEZkAAOAAM8cRzINxI4mOzqbgCmt9IhAEdI-BPVCMSgRgBaKBwUWinUBNs4AKe2U+pCM2MC5mMHC8hT8iHCENwACCKqUOU1cGRDdpA5jcZA65UoYjUfpM79EJgvRyeYLEmQACYH8ScBRuQBhJhp0FYQAFYdXQZkWCfbtZzPa1+QASSEBAYDodMcGhTchDoaZQ1POA1DUIA)
<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**
<!-- prettier-ignore -->
```tsx
function getHello<SomeType extends { hello: string }>(
  this: SomeType
): SomeType["hello"] {
  return this.hello;
}

const specificObject = {
  hello: "world",
  foo: "bar",
} as const;

type SpecificType = typeof specificObject;

const getBoundHello = (getHello<SpecificType>).bind(specificObject);
```

**Output:**
<!-- prettier-ignore -->
```tsx
function getHello<SomeType extends { hello: string }>(
  this: SomeType
): SomeType["hello"] {
  return this.hello;
}

const specificObject = {
  hello: "world",
  foo: "bar",
} as const;

type SpecificType = typeof specificObject;

const getBoundHello = getHello<SpecificType>.bind(specificObject);

```

```diff
-const getBoundHello = (getHello<SpecificType>).bind(specificObject); // no TS error
+const getBoundHello = getHello<SpecificType>.bind(specificObject); // TS error
```

Pasting [before](https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAcwKZQBKoDbbgHgFkBPAFWIAdVFUAPKVMAEwGdEBvRACxzwC5ELKACcYYZIgC+APgAUAKESIoXGCwElyVeQEoNZSqgDaAIh644JgLodFiYehDCkKtQDpzeANzzJ8+RAIQoJUEDDAMBAA8gBGAFao0IgAvLZKnnACJgDucMLYTCYANHbAcJmIJjEAhsLFvojVbIFgQj4BQVAo6ABCcOBMWBYpiLJomLwEUIZwwCGJ4ZGxCdDSOm4xYkyyLKGL0fGJUDo+QA) / [after](https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAcwKZQBKoDbbgHgFkBPAFWIAdVFUAPKVMAEwGdEBvRACxzwC5ELKACcYYZIgC+APgAUAKESIoXGCwElyVeQEoNZSqgDaAIh644JgLodFiYehDCkKtQDpzeANzzJ8+RAIQoJUEDDAMBAA8gBGAFao0IgAvLZKnnACJgDucMLYTCYANHbAcJmIJjEAhsLFvojVbIFgQj4BQVAo6ABCcOBMWBYp3Zi8BFCGcMAhieGRsQnQ0m4xYkyyLKHz0fGJUDo+QA) into the TypeScript playground produces the following error in the latter case:

```txt
An instantiation expression cannot be followed by a property access.
```

My example is quite clumsy, so if you can come up with a concise and clear one, that can help. Either way, this is _probably_ a bug and Prettier should keep the parentheses. Thanks for reporting!

Patch:
diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js
--- a/src/language-js/needs-parens.js
+++ b/src/language-js/needs-parens.js
@@ -890,6 +890,9 @@ function needsParens(path, options) {
           parent.type !== "VariableDeclarator" &&
           parent.type !== "YieldExpression")
       );
+
+    case "TSInstantiationExpression":
+      return key === "object" && isMemberExpression(parent);
   }
 
   return false;


Test patch:
diff --git a/tests/format/typescript/instantiation-expression/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/instantiation-expression/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/typescript/instantiation-expression/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/typescript/instantiation-expression/__snapshots__/jsfmt.spec.js.snap
@@ -103,6 +103,46 @@ new A<T>();
 ================================================================================
 `;
 
+exports[`property-access.ts format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+(Array<string>).name;
+(fn1<string>).bind(obj);
+(fn2<string, number>).bind(obj);
+a[(Array<string>)];
+a[(Array<string>).name];
+(Array<string>).a;
+(Array<string>)?.a;
+(Array<string>)[a];
+(Array<string>)?.[a];
+(Array<string>)["a"];
+(Array<string>)?.["a"];
+(Array<string>)[\`a\`];
+(Array<string>)?.[\`a\`];
+(Array<string>)[(Array<string>)];
+
+=====================================output=====================================
+(Array<string>).name;
+(fn1<string>).bind(obj);
+(fn2<string, number>).bind(obj);
+a[Array<string>];
+a[(Array<string>).name];
+(Array<string>).a;
+(Array<string>)?.a;
+(Array<string>)[a];
+(Array<string>)?.[a];
+(Array<string>)["a"];
+(Array<string>)?.["a"];
+(Array<string>)[\`a\`];
+(Array<string>)?.[\`a\`];
+(Array<string>)[Array<string>];
+
+================================================================================
+`;
+
 exports[`typeof.ts format 1`] = `
 ====================================options=====================================
 parsers: ["typescript"]
diff --git a/tests/format/typescript/instantiation-expression/property-access.ts b/tests/format/typescript/instantiation-expression/property-access.ts
new file mode 100644
--- /dev/null
+++ b/tests/format/typescript/instantiation-expression/property-access.ts
@@ -0,0 +1,14 @@
+(Array<string>).name;
+(fn1<string>).bind(obj);
+(fn2<string, number>).bind(obj);
+a[(Array<string>)];
+a[(Array<string>).name];
+(Array<string>).a;
+(Array<string>)?.a;
+(Array<string>)[a];
+(Array<string>)?.[a];
+(Array<string>)["a"];
+(Array<string>)?.["a"];
+(Array<string>)[`a`];
+(Array<string>)?.[`a`];
+(Array<string>)[(Array<string>)];