类型“1”不可分配给类型 'T[Extract<keyof T, string>]'
Type '1' is not assignable to type 'T[Extract<keyof T, string>]'
我有一个以下函数,它接受一个扩展 Foo(它是一个对象)的类型 T 的参数。在函数中,它遍历给定对象的每个键以创建一个具有完全相同键但对应值均为 1 的新对象(这个函数做什么并不重要)。
但是使用Type '1' is not assignable to type 'T[Extract<keyof T, string>]'.
编译失败。我认为 T[Extract<keyof T, string>]
是 number
,所以分配 1
即 number
应该可行。
我的代码有什么问题?
type Foo = {
[key: string]: number
}
const func = <T extends Foo>(obj: T): T => {
for (const name in obj) {
obj[name] = 1
}
return obj
}
编译器通常不会对泛型类型的操作进行非常复杂的分析(即,在 func()
的实现中依赖于未解析类型参数的类型,如 T
)...它往往更擅长处理更直接的具体类型(如 Foo
)。
因此编译器非常满意并允许您的函数的以下具体版本:
const concreteFunc = (obj: Foo): Foo => {
for (const name in obj) {
obj[name] = 1; // okay
}
return obj; // okay
};
由于尚不知道未解析的泛型类型,编译器将不太确定您正在做的事情是否安全,并可能会发出警告。此警告并不一定意味着您肯定 犯了错误。
这种情况经常发生在泛型函数的实现内部。如果你仔细分析你在做什么并确定它确实是类型安全的,你可以使用 type assertions 来删除警告。
例如,您可以这样做:
const func = <T extends Foo>(obj: T): T => {
for (const name in obj) {
obj[name] = 1 as T[typeof name]; // assert BUT BEWARE ☠
}
return obj;
};
但请注意,类型断言意味着类型安全的责任已从编译器转移到您...并且(回答您的问题)这不安全 .
这就是为什么……考虑以下代码:
interface Bar extends Foo {
two: 2;
four: 4;
six: 6;
eight: 8;
}
const bar: Bar = {
two: 2,
four: 4,
six: 6,
eight: 8
};
const b = func(bar);
console.log(b.two); // 2 at compile time, but prints 1!
console.log(b.four); // 4 at compile time, but prints 1!
console.log(b.six); // 4 at compile time, but prints 1!
console.log(b.eight); // 4 at compile time, but prints 1!
这里我们看到一个接口 Bar
,它通过添加值为 numeric literals 的已知属性扩展 Foo
,其中 none 等于 1
.当我们调用 func(bar)
时,T
被推断为 Bar
,因此 func(bar)
的输出也应该是 Bar
.
坏事发生了。我们有一个对象,其已知属性在编译时应该是偶数,但实际上在运行时是 1
。
所以这就是为什么您可能不应该在像 func()
这样的函数中使用断言的原因。可能有一种真正安全的方式来编写 func()
... 比如,可能是这样的:
const funcSafer = <
T extends { [K in keyof T]: 1 extends T[K] ? unknown : never }
>(
obj: T
): T => {
for (const name in obj) {
obj[name] = 1 // error! still need "as T[typeof name]"
}
return obj;
};
这里,对T
的约束具体是1
应该可以分配给它的所有属性。这具有以下理想效果:
funcSafer(bar); // error! property "two" is incompatible
const foo: Foo = {two: 2, four: 4}; // just Foo, not Bar
funcSafer(foo); // okay
funcSafer({a: 1 as 1}); // okay
funcSafer({a: 4}); // okay, interpreted as {a: number}
funcSafer({a: 4 as 4}); // error, "a" is incompatible
当然,编译器仍然无法判断 obj[name] = 1
在实现中是安全的。它太复杂了...所以我们需要断言。
好的,希望对您有所帮助。祝你好运!
我有一个以下函数,它接受一个扩展 Foo(它是一个对象)的类型 T 的参数。在函数中,它遍历给定对象的每个键以创建一个具有完全相同键但对应值均为 1 的新对象(这个函数做什么并不重要)。
但是使用Type '1' is not assignable to type 'T[Extract<keyof T, string>]'.
编译失败。我认为 T[Extract<keyof T, string>]
是 number
,所以分配 1
即 number
应该可行。
我的代码有什么问题?
type Foo = {
[key: string]: number
}
const func = <T extends Foo>(obj: T): T => {
for (const name in obj) {
obj[name] = 1
}
return obj
}
编译器通常不会对泛型类型的操作进行非常复杂的分析(即,在 func()
的实现中依赖于未解析类型参数的类型,如 T
)...它往往更擅长处理更直接的具体类型(如 Foo
)。
因此编译器非常满意并允许您的函数的以下具体版本:
const concreteFunc = (obj: Foo): Foo => {
for (const name in obj) {
obj[name] = 1; // okay
}
return obj; // okay
};
由于尚不知道未解析的泛型类型,编译器将不太确定您正在做的事情是否安全,并可能会发出警告。此警告并不一定意味着您肯定 犯了错误。
这种情况经常发生在泛型函数的实现内部。如果你仔细分析你在做什么并确定它确实是类型安全的,你可以使用 type assertions 来删除警告。
例如,您可以这样做:
const func = <T extends Foo>(obj: T): T => {
for (const name in obj) {
obj[name] = 1 as T[typeof name]; // assert BUT BEWARE ☠
}
return obj;
};
但请注意,类型断言意味着类型安全的责任已从编译器转移到您...并且(回答您的问题)这不安全 .
这就是为什么……考虑以下代码:
interface Bar extends Foo {
two: 2;
four: 4;
six: 6;
eight: 8;
}
const bar: Bar = {
two: 2,
four: 4,
six: 6,
eight: 8
};
const b = func(bar);
console.log(b.two); // 2 at compile time, but prints 1!
console.log(b.four); // 4 at compile time, but prints 1!
console.log(b.six); // 4 at compile time, but prints 1!
console.log(b.eight); // 4 at compile time, but prints 1!
这里我们看到一个接口 Bar
,它通过添加值为 numeric literals 的已知属性扩展 Foo
,其中 none 等于 1
.当我们调用 func(bar)
时,T
被推断为 Bar
,因此 func(bar)
的输出也应该是 Bar
.
坏事发生了。我们有一个对象,其已知属性在编译时应该是偶数,但实际上在运行时是 1
。
所以这就是为什么您可能不应该在像 func()
这样的函数中使用断言的原因。可能有一种真正安全的方式来编写 func()
... 比如,可能是这样的:
const funcSafer = <
T extends { [K in keyof T]: 1 extends T[K] ? unknown : never }
>(
obj: T
): T => {
for (const name in obj) {
obj[name] = 1 // error! still need "as T[typeof name]"
}
return obj;
};
这里,对T
的约束具体是1
应该可以分配给它的所有属性。这具有以下理想效果:
funcSafer(bar); // error! property "two" is incompatible
const foo: Foo = {two: 2, four: 4}; // just Foo, not Bar
funcSafer(foo); // okay
funcSafer({a: 1 as 1}); // okay
funcSafer({a: 4}); // okay, interpreted as {a: number}
funcSafer({a: 4 as 4}); // error, "a" is incompatible
当然,编译器仍然无法判断 obj[name] = 1
在实现中是安全的。它太复杂了...所以我们需要断言。
好的,希望对您有所帮助。祝你好运!