microsoft / microsoft/TypeScript
Suggestion: Allow local types to be declared in interfaces
还没有人认领这个 Issue。
- 主要语言
- Go
- 星标
- 111k
- 派生
- 14.3k
- 平均合并
- 2 天 4 小时
- 30 天内合并 PR
- 132
描述
I have something like this:
export interface Reducer<State, Types extends Action<string, any>> {
add<TypeName extends string, Payload>(action: {
type: TypeName,
reduce: (state: State, action: Payload) => State
}): Reducer<State, Types | Action<TypeName, Payload>>;
readonly cursorType: Cursor<State, Types>;
}
The details aren't that important except to illustrate that a Reducer is immutable, and has an add method that returns another Reducer, but see that the return type has something extra "unioned" into it. By repeated chained calls to add I can build up a big nasty old type that would be ugly to have fully declare by hand. Fortunately type inference takes care of building the type for me, which is great.
Then elsewhere in my code I want to be able to declare something called a "cursor", which needs to have a type that corresponds to the reducer's type. The cursor could be a field in a class so I need to be able to refer to the type so I can declare such a field.
So I want to provide a simple way to declare a const of the type "correct kind of cursor for a given reducer", leveraging the work that the TS compiler already did for me with its type inference.
My slightly hacky approach, as shown above, is to declare a readonly field cursorType. The value of this is at runtime is junk and should not be used! So I need a "here be dragons" comment on it. Its only purpose is to be prefixed with typeof, e.g.:
const R = getReducerSomehow();
class Test {
constructor(public readonly myCursor: typeof R.cursorType) { }
}
To fill in the cursorType field of a Reducer I have to do this filth:
newReducer.cursorType = {} as Cursor<State, Types>;
So cursorType really should never be used as a value. It doesn't even need to exist as a value. It will cause a runtime error if anyone tries to used it as a cursor. Ugh. But how else can I make this elaborately computed type available conveniently?
I'm wondering if TS could allow:
export interface Reducer<State, Types extends Action<string, any>> {
add<TypeName extends string, Payload>(action: {
type: TypeName,
reduce: (state: State, action: Payload) => State
}): Reducer<State, Types | Action<TypeName, Payload>>;
// not currently possible:
type CursorType = Cursor<State, Types>;
}
i.e. a type alias can be added to an interface. So now my implementation of Reducer no longer has to do anything. No nasty dummy runtime variable hack required.
And my usage example becomes:
const R = getReducerSomehow();
class Test {
constructor(public readonly myCursor: R.CursorType) { }
}
That is, CursorType is a type that can be referred to as if it was a member of an instance. Similar I guess to:
namespace N {
export type S = string;
}
const s: N.S = "hi";
In which N is an object at runtime and yet can also be used to find the type S.
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
从 issue 中的 Reducer、Cursor 和 CursorType được đề xuất示例开始,然后研究 TypeScript 如何表示接口成员和类型别名。确定实例限定类型别名所需的编译器入口点和测试;完成标准是:提议的语法能够通过类型检查而无需运行时字段,同时保留现有的接口行为。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- typescript
- 领域
- compilers
- Issue 类型
- 功能
- 难度
- 5/5
- 预计耗时
- 一周以上
- 活跃度
- 停滞
- 描述清晰度
- 基本清楚
- 新手友好度
- 25/100