我有一个TypeScrip接口,我想引用它自己.手工构造很繁琐,所以我编写了一个helper函数.

function insertSelf<T, K extends keyof T>(prop: K, o: Omit<T, K>): T {
    return (o as any)[prop] = o as T // close-enough
}

然后给出一个实例,我可以在一条语句中构造并自赋值它.

interface Foo {
    a: number,
    b: number,
    c: this
}

const f: Foo = insertSelf<Foo, 'c'>('c', {a: 1, b: 2})
// f.c points back to f

不幸的是,必须将参数名同时作为literal type和字符串文字值进行重复.问题是,是否有任何通用 struct 可以避免这种情况.我猜答案是否定的,至少不会牺牲编译器对属性名或其他记录字段的判断.

我发现的最好的 Select 是事先通过绑定来简化调用.

const insertFoo = insertSelf<Foo, 'c'>.bind(null, 'c')
const f: Foo = insertFoo({a: 1, b: 2})

推荐答案

对于generics,您的版本不能很好地使用TypeScrip的推理算法,并且迫使您手动指定泛型类型参数,从而导致重复.理想情况下,您希望类型判断器推断您的类型,但从类型Omit<T, K>的值推断T基本上是行不通的(the Omit utility type是根据conditional type实现的,而TypeScrip实际上不能对泛型条件类型进行太多分析).


相反,您应该考虑使推断尽可能简单,方法是要求编译器从类型为T的值推断出T.它看起来是这样的:

function insertSelf<T, K extends PropertyKey>(prop: K, o: T): ⋯ 

然后剩下的就是用TK来描述返回类型了.这里有一种方法:

type Self<K extends PropertyKey, T> =
    T & { [P in K]: Self<K, T> }

因此,Self<K, T>是类型T的值,它在键为K的属性处也具有类型Self<K, T>的属性.这是一种递归类型,描述的关系非常类似于您在Foo中使用的polymorphic this type.

实现方式可能是

function insertSelf<T, K extends PropertyKey>(prop: K, o: T): Self<K, T>;
function insertSelf(prop: PropertyKey, o: any) {
    return o[prop] = o;
}

在这里,我只是使用单个调用签名overload来 suppress 来自编译器的任何错误,而不理解return o[prop]=o如何工作或与Self<K, T>相关.


让我们试试看:

const f = insertSelf('c', { a: 1, b: 2 });
// const f: Self<"c", { a: number;  b: number; }>
console.log(f.a.toFixed(2)) // 1.00
console.log(f.c.c.c.b.toFixed(2)) // 2.00

看起来不错,f的类型是Self<"c", {a: number, b: number}>,因此它具有您期望的所有属性.

另请注意,这并不妨碍您使用包含this属性类型的自定义接口:

interface Foo {
    a: number,
    b: number,
    c: this
}
const foo: Foo = f; // okay

该赋值之所以成功,是因为编译器能够验证Self<"c", { a: number; b: number; }>是否可赋值给Foo.因此,如果你对Self机型不满意,你可以 Select .

Playground link to code

Typescript相关问答推荐

Angular 17 -如何使用新的@if语法在对象中使用Deliverc值

APP_INITIALIZER—TypeError:appInits不是函数

angular 17独立使用多个组件(例如两个组件)

替代语法/逻辑以避免TS变量被分配之前使用." "

JS Redux Devtools扩展不工作

在TypeScript中使用泛型的灵活返回值类型

如果存在ts—mock—imports,我们是否需要typescpt中的IoC容器

如何在排版中正确键入中间件链和控制器链

将对象属性转换为InstanceType或原样的强类型方法

无法绑定ngModel.条件ngSwitchCase中的错误

如何在TypeScrip中从字符串联合类型中省略%1值

Material UI / MUI系统:我如何告诉TypeScript主题是由提供程序传递的?

Fp-ts使用任务的最佳方法包装选项

如何将TS泛型用于react-hook-form接口?

如何缩小函数的返回类型?

在Google授权后由客户端接收令牌

作为参数的KeyOf变量的类型

通过辅助函数获取嵌套属性时保留类型

递归JSON类型脚本不接受 node 值中的变量

如何使用 AWS CDK 扩展默认 ALB 控制器策略?