'{ host: string | 类型的参数不明确的;用户:字符串 | undefined ... }' 不可分配给 'string | ConnectionConfig' 类型的参数
Argument of type '{ host: string | undefined; user: string | undefined ... }' is not assignable to parameter of type 'string | ConnectionConfig'
我正在创建与 AWS mysql 数据库的连接,如下所示:
const config = {
host: process.env.RDS_HOSTNAME,
user: process.env.RDS_USERNAME,
password: process.env.RDS_PASSWORD,
port: 3306,
database: process.env.RDS_DB_NAME,
}
const db = mysql.createConnection(config) // config gets highlighted
但我收到以下错误:
Argument of type '{ host: string | undefined; user: string | undefined; password: string | undefined; port: number; database: string | undefined; }' is not assignable to parameter of type 'string | ConnectionConfig'.
Type '{ host: string | undefined; user: string | undefined; password: string | undefined; port: number; database: string | undefined; }' is not assignable to type 'ConnectionConfig'.
Types of property 'host' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2345)
早些时候,我遇到来自 .env
的端口问题。当我切换到对端口进行硬编码时,我得到了这个。
我不明白问题是什么,也不知道如何解决。
问题是 process.env
在 @types/node
中声明为:
// process.d.ts
...
interface ProcessEnv extends Dict<string> {}
...
env: ProcessEnv
// global.d.ts
interface Dict<T> {
[key: string]: T | undefined;
}
...
您可能会注意到 env
中的任何查找结果都是 string | undefined
。虽然 createConnection
预计 string
至少 host
属性.
您有多种选择来确保通过 config
的编译器正常:
- 如果您完全确定所有环境变量都已正确设置,只需对您的配置进行类型转换:
type NoUndefinedField<T> = {
[P in keyof T]: Exclude<T[P], undefined>;
};
createConnection(config as NoUndefinedField<typeof config>)
更新
快速解释:
这里我们使用 <T>
generics to abstract over concrete type, mapped type [P in keyof T]
to iterate through all properties ([P in keyof T
]) of type T
and built-in utility type Exclude<>
to remove undesired types from each property of type T[K]
。
在我们从 typeof config
we typecast config
值的属性中删除所有 undefined
类型到此类型之后。
分步说明:
所以,我们有一个对象(值)config
,类型为:
type ConfigUndefined = {
host: string | undefined,
username: string | undefined,
port: number,
}
declare const config: ConfigUndefined
但我们需要一个 mysql
create 方法可以消化的另一种类型的对象:
type Config = { // defined somewhere inside `mysql` library
host: string,
username: string,
port: number,
}
确保编译器我们 更好地了解我们的值在运行时的类型的最快方法是type assertion。我们从 mysql
库中导入 Config
类型,并断言编译器我们的 config
对象具有与预期完全相同的类型:
mysql.createConnection(config as Config)
到目前为止一切顺利。 createConnection
满意了,可以到此为止了。尽管这种方法很脆弱。如果 mysql 的库维护者稍后将另一个必需的 属性 添加到 Config
类型怎么办?我们的代码仍然断言编译器我们的 config: ConfigUndefined
值具有 绝对 与 Config
相同的类型,但实际上它不再是
declare function createConnection(config: Config): void
declare const config: ConfigUndefined
type ConfigUndefined = {
host: string | undefined,
username: string | undefined,
port: number
}
type Config = {
host: string,
username: string,
port: number,
smthing: string
}
createConnection(config as Config) // ok. compiler is stil happy
好吧,在运行时错误之后我们可以调查问题,将 smthing: process.env.SMTHING
添加到我们的 config
对象中,我们又好了。下次见。
如果我们在编译时得到那个错误不是更好吗?甚至没有 运行 我们的代码?所以我们不能直接依赖mysql的Config
类型。我们应该让编译器检查我们的真实类型是否可分配(兼容) Config
。为了让它工作,我们应该让编译器计算你的 config
对象类型 但 的确切形状,而不需要每个 属性 中那些讨厌的 undefined
类型.
我们应该以某种方式遍历 ConfigUndefined
类型的每个 属性 并删除 undefined
类型(如果有的话)。
让我们从一个简单的类型开始。 string | undefined
是 union type. To remove undefined
from string | undefined
type we can use conditional types and namely their distribution 属性 为:
type StringOrUndefined = string | undefined
type RemoveUndefined<T> = T extends undefined ? never : T
type StringWoUndefined = RemoveUndefined<StringOrUndefined>
// type StringWoUndefined ~ string
幸运的是,我们在标准库中有 Exclude
实用程序类型,它对我们想要排除的任意类型完全相同。我们可以将 StringWoUndefined
类型重写为:
type StringOrUndefined = string | undefined
type StringWoUndefined = Exclude<StringOrUndefined, undefined>
现在我们必须遍历 ConfigUndefined
个属性并从每个属性中删除 undefined
。这就是 mapped types and keyof
运算符派上用场的地方:
type ConfigWoUndefined = {
// keyof ConfigUndefined = "host" | "username" | "port"
[K in keyof ConfigUndefined]: Exclude<ConfigUndefined[K], undefined>
}
// type ConfigWoUndefined = {
// host: string,
// username: string,
// port: number,
// }
此处对于 "host" | "username" | "port"
中的每个 K
打字稿计算 ConfigUndefined[K]
indexed access type 并在 Exclude
实用程序类型的帮助下删除 undefined
类型来自它。
尽管该类型看起来非常有用,如果我们想重用相同的功能,我们将不得不再次重新输入它,仅将 ConfigUndefined
更改为我们想要删除的其他类型 undefined
s从。 generic types 看起来不错。这是一种类型级别的函数,它接受类型参数和 return 另一个修改后的类型:
// our `old` ConfigWoUndefined
// type ConfigWoUndefined = {
// [K in keyof ConfigUndefined]: Exclude<ConfigUndefined[K], undefined>
// }
type NoUndefinedField<T> = {
[K in keyof T]: Exclude<T[K], undefined>
}
type ConfigWoUndefined = NoUndefinedField<ConfigUndefined>
到目前为止一切顺利。但是我们仍然有一个不同的对象(值)config
和类型 ConfigUndefined
。我们必须让它们保持同步。这就是 typeof
运算符的用途:
declare const process: { env: { [k: string]: string | undefined } }
const config = {
host: process.env.HOST,
username: process.env.USER,
port: 3000,
}
type A = typeof config
// type A = {
// host: string | undefined,
// username: string | undefined,
// port: number,
// }
我们终于可以摆脱 ConfigUndefined
类型了。
所以总结一下:
createConnection(config as NoUndefinedField<typeof config>)
在这里,我们使用 typeof config
获取值 config
的确切类型,从其每个属性中删除 undefined
使用 NonUndefinedField
类型并强制转换我们的 config
值到此修改后的类型。这使得打字稿检查 NoUndefinedField<typeof config>
是否可分配给 mysql 的 Config
预期类型。
如果我们绝对确定所有必需的 env
都按应有的方式定义,那么这可能是有道理的。更好的做法仍然是使用运行时检查 narrow 类型。就像下面在断言函数中所做的那样。
- 为配置实施正确的assertion function:
function assertConfig<T>(config: T): asserts config is NoUndefinedField<T> {
for (const key in config) {
if (typeof config[key] === 'undefined')
throw new Error(`Config check failed. Key "${key}" is undefined.`)
}
}
- 实施自定义 type guard or use other type narrowing 技术以排除
undefined
值
不过,我相信生产代码断言功能是在数据库配置错误时提前退出的正确方法。
我正在创建与 AWS mysql 数据库的连接,如下所示:
const config = {
host: process.env.RDS_HOSTNAME,
user: process.env.RDS_USERNAME,
password: process.env.RDS_PASSWORD,
port: 3306,
database: process.env.RDS_DB_NAME,
}
const db = mysql.createConnection(config) // config gets highlighted
但我收到以下错误:
Argument of type '{ host: string | undefined; user: string | undefined; password: string | undefined; port: number; database: string | undefined; }' is not assignable to parameter of type 'string | ConnectionConfig'.
Type '{ host: string | undefined; user: string | undefined; password: string | undefined; port: number; database: string | undefined; }' is not assignable to type 'ConnectionConfig'.
Types of property 'host' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2345)
早些时候,我遇到来自 .env
的端口问题。当我切换到对端口进行硬编码时,我得到了这个。
我不明白问题是什么,也不知道如何解决。
问题是 process.env
在 @types/node
中声明为:
// process.d.ts
...
interface ProcessEnv extends Dict<string> {}
...
env: ProcessEnv
// global.d.ts
interface Dict<T> {
[key: string]: T | undefined;
}
...
您可能会注意到 env
中的任何查找结果都是 string | undefined
。虽然 createConnection
预计 string
至少 host
属性.
您有多种选择来确保通过 config
的编译器正常:
- 如果您完全确定所有环境变量都已正确设置,只需对您的配置进行类型转换:
type NoUndefinedField<T> = {
[P in keyof T]: Exclude<T[P], undefined>;
};
createConnection(config as NoUndefinedField<typeof config>)
更新
快速解释:
这里我们使用 <T>
generics to abstract over concrete type, mapped type [P in keyof T]
to iterate through all properties ([P in keyof T
]) of type T
and built-in utility type Exclude<>
to remove undesired types from each property of type T[K]
。
在我们从 typeof config
we typecast config
值的属性中删除所有 undefined
类型到此类型之后。
分步说明:
所以,我们有一个对象(值)config
,类型为:
type ConfigUndefined = {
host: string | undefined,
username: string | undefined,
port: number,
}
declare const config: ConfigUndefined
但我们需要一个 mysql
create 方法可以消化的另一种类型的对象:
type Config = { // defined somewhere inside `mysql` library
host: string,
username: string,
port: number,
}
确保编译器我们 更好地了解我们的值在运行时的类型的最快方法是type assertion。我们从 mysql
库中导入 Config
类型,并断言编译器我们的 config
对象具有与预期完全相同的类型:
mysql.createConnection(config as Config)
到目前为止一切顺利。 createConnection
满意了,可以到此为止了。尽管这种方法很脆弱。如果 mysql 的库维护者稍后将另一个必需的 属性 添加到 Config
类型怎么办?我们的代码仍然断言编译器我们的 config: ConfigUndefined
值具有 绝对 与 Config
相同的类型,但实际上它不再是
declare function createConnection(config: Config): void
declare const config: ConfigUndefined
type ConfigUndefined = {
host: string | undefined,
username: string | undefined,
port: number
}
type Config = {
host: string,
username: string,
port: number,
smthing: string
}
createConnection(config as Config) // ok. compiler is stil happy
好吧,在运行时错误之后我们可以调查问题,将 smthing: process.env.SMTHING
添加到我们的 config
对象中,我们又好了。下次见。
如果我们在编译时得到那个错误不是更好吗?甚至没有 运行 我们的代码?所以我们不能直接依赖mysql的Config
类型。我们应该让编译器检查我们的真实类型是否可分配(兼容) Config
。为了让它工作,我们应该让编译器计算你的 config
对象类型 但 的确切形状,而不需要每个 属性 中那些讨厌的 undefined
类型.
我们应该以某种方式遍历 ConfigUndefined
类型的每个 属性 并删除 undefined
类型(如果有的话)。
让我们从一个简单的类型开始。 string | undefined
是 union type. To remove undefined
from string | undefined
type we can use conditional types and namely their distribution 属性 为:
type StringOrUndefined = string | undefined
type RemoveUndefined<T> = T extends undefined ? never : T
type StringWoUndefined = RemoveUndefined<StringOrUndefined>
// type StringWoUndefined ~ string
幸运的是,我们在标准库中有 Exclude
实用程序类型,它对我们想要排除的任意类型完全相同。我们可以将 StringWoUndefined
类型重写为:
type StringOrUndefined = string | undefined
type StringWoUndefined = Exclude<StringOrUndefined, undefined>
现在我们必须遍历 ConfigUndefined
个属性并从每个属性中删除 undefined
。这就是 mapped types and keyof
运算符派上用场的地方:
type ConfigWoUndefined = {
// keyof ConfigUndefined = "host" | "username" | "port"
[K in keyof ConfigUndefined]: Exclude<ConfigUndefined[K], undefined>
}
// type ConfigWoUndefined = {
// host: string,
// username: string,
// port: number,
// }
此处对于 "host" | "username" | "port"
中的每个 K
打字稿计算 ConfigUndefined[K]
indexed access type 并在 Exclude
实用程序类型的帮助下删除 undefined
类型来自它。
尽管该类型看起来非常有用,如果我们想重用相同的功能,我们将不得不再次重新输入它,仅将 ConfigUndefined
更改为我们想要删除的其他类型 undefined
s从。 generic types 看起来不错。这是一种类型级别的函数,它接受类型参数和 return 另一个修改后的类型:
// our `old` ConfigWoUndefined
// type ConfigWoUndefined = {
// [K in keyof ConfigUndefined]: Exclude<ConfigUndefined[K], undefined>
// }
type NoUndefinedField<T> = {
[K in keyof T]: Exclude<T[K], undefined>
}
type ConfigWoUndefined = NoUndefinedField<ConfigUndefined>
到目前为止一切顺利。但是我们仍然有一个不同的对象(值)config
和类型 ConfigUndefined
。我们必须让它们保持同步。这就是 typeof
运算符的用途:
declare const process: { env: { [k: string]: string | undefined } }
const config = {
host: process.env.HOST,
username: process.env.USER,
port: 3000,
}
type A = typeof config
// type A = {
// host: string | undefined,
// username: string | undefined,
// port: number,
// }
我们终于可以摆脱 ConfigUndefined
类型了。
所以总结一下:
createConnection(config as NoUndefinedField<typeof config>)
在这里,我们使用 typeof config
获取值 config
的确切类型,从其每个属性中删除 undefined
使用 NonUndefinedField
类型并强制转换我们的 config
值到此修改后的类型。这使得打字稿检查 NoUndefinedField<typeof config>
是否可分配给 mysql 的 Config
预期类型。
如果我们绝对确定所有必需的 env
都按应有的方式定义,那么这可能是有道理的。更好的做法仍然是使用运行时检查 narrow 类型。就像下面在断言函数中所做的那样。
- 为配置实施正确的assertion function:
function assertConfig<T>(config: T): asserts config is NoUndefinedField<T> {
for (const key in config) {
if (typeof config[key] === 'undefined')
throw new Error(`Config check failed. Key "${key}" is undefined.`)
}
}
- 实施自定义 type guard or use other type narrowing 技术以排除
undefined
值
不过,我相信生产代码断言功能是在数据库配置错误时提前退出的正确方法。