TypeScript Rename / Find All References should find properties in indirectly contextually-typed object literals

ki1q1bka  于 6个月前  发布在  TypeScript
关注(0)|答案(8)|浏览(58)

Bug报告

🔎 搜索词

  • object
  • 通用函数
  • 参数
  • 参数化类型
  • 重命名
  • 转到定义
  • 查找引用

🕗 版本与回归信息

  • 在所有我尝试的版本中,这种行为都是如此,我也查看了关于上下文类型的文章中的常见问题解答。

⏯ Playground链接

💻 代码

希望对象具有类型注解,以便语言服务器功能可以在对象属性上正确工作,例如“重命名”/“转到定义”/“查找引用”。
以下示例都有类型注解,但这些语言服务器功能不起作用。
这似乎是因为上下文类型没有传递给提供给通用函数的参数。
是否有可能改变这种行为,使上下文类型能够传递?特别是当参数是一个对象字面量表达式时。

declare const identity: <T>(x: T) => T;

type User = { name: string };

const value: User = identity({ name: "bob" });

正如您在这个演示中看到的,如果我们重命名一个属性,重命名不会传播。这是因为 TypeScript 似乎无法理解对象字面量表达式和类型之间的关系,尽管我们已经在对象定义附近提供了一个非常接近的类型注解。
Screen.Recording.2022-09-20.at.18.05.03.mov
一个经常出现的真实世界例子是当我们使用 O.some 函数时。

import * as O from "fp-ts/Option";

type User = { name: string };

const value: O.Option<User> = O.some({ name: "bob" });

另一个经常在使用 pipe 函数(如在 fp-ts 中定义的)时出现的真实世界例子是:

declare function pipe<A, B>(a: A, ab: (a: A) => B): B;

type User = { name: string };
declare const logName: (user: User) => void;

pipe({ name: "foo" }, logName);
dohp0rv5

dohp0rv51#

这似乎是因为上下文类型没有流向提供给泛型函数的参数。
上下文类型确实流向;证明:

declare const identity: <T>(x: T) => T;

type User = { name: (x: string) => void };

// Incorrect argument correctly flagged
const value: User = identity({ name: a => a.toUpperCase(3) });

你描述的问题是否存在类型系统观察,还是只是 find all / rename 没有按预期工作?

h79rfbju

h79rfbju2#

我错了——你是对的!
问题是find all / rename没有按预期工作吗?
只是这样。
出于兴趣,如果上下文类型确实如你所指出的那样流动,为什么find all / rename不起作用?我想{ name: "bob" }的值没有给定User的类型——TypeScript只是检查它是否可以分配给上下文类型User吗?

vh0rcniy

vh0rcniy3#

你描述的问题是否有类型系统的观察?
实际上,我认为这可能是一个?

declare const identity: <T>(x: T) => T;

type User = { name: string; age?: number };

const value: User = identity({
    name: "bob",
    // Typo. This should be flagged as an error, but it's not.
    ageeeeee: 42,
});
ddhy6vgd

ddhy6vgd4#

这是一个不同的问题(我认为...),identity首先从其参数中推断出其返回类型,然后其返回类型最终可以分配给{ name: string, age?: number },因此没有多余的属性错误,因为到那时对象字面量不再被认为是“新鲜”的(即它正在检查函数的返回值,而不是直接的对象字面量)。上下文类型User作为推断候选项的优先级可能低于您实际提供的参数。
基本上,多余的属性检查不是正常类型检查流程的一部分,通常依赖于显式类型注解;让您让类型推断的情况通常会绕过它们。

cygmwpex

cygmwpex5#

上下文类型化不会过度检查属性(可能令人惊讶)

1zmg4dgp

1zmg4dgp6#

declare const identity: <T>(x: T) => T;

// Rename here renames alice but not bob
type User = { name: string };

const a: User = { name: "alice" };
const b: User = identity({ name: "bob" });
5cnsuln7

5cnsuln77#

感谢您的澄清和更新这个问题。
关于我描述的过度属性的行为,您有什么想法吗?这个是否也可以改变/修复?我怀疑这已经是一个其他问题了。
另外,但相关的是,我正在尝试编写一个lint规则来要求对象的类型注解。我的动机和我的工作进度可以在这里找到:typescript-eslint/typescript-eslint#5666 。我已经通过检查 ObjectLiteralExpression 是否有上下文类型并在没有的情况下报告一个lint错误取得了很好的进展,但在上面的场景中,我们将一个对象传递给一个泛型函数时,这种方法不起作用:似乎对象确实有一个上下文类型,但那个上下文类型似乎是从对象本身(而不是context)推断出来的。
这是我目前为止所拥有的内容。您认为这是正确的吗?还是有另一种方法可以捕获这种情况?

const checkObjectHasNoType = (n: ts.ObjectLiteralExpression): boolean => {
  const contextualType = checker.getContextualType(n);
  return (
    contextualType === undefined ||
    /**
* Contextual type is inferred from this object node.
*
* Note: if two nodes are the same node they will be equal by reference.
*
* Examples:
* - object passed as a function argument where the parameter type is generic, e.g.
*   `declare const f: <T>(x: T) => T; f({ prop: 1 });``
* - object as a function return value where the return type is generic, e.g.
*   `[].map(() => ({ prop: 1 }))`
*/
    n === contextualType.getSymbol()?.valueDeclaration
  );
};
mzsu5hc0

mzsu5hc08#

这取决于你想要发生什么,但这似乎是正确的方法。

相关问题