以编程方式为我的环境中的环境键声明打字稿类型
Programmatically declare typescript types for environment keys in my env
假设我得到了这个 .env.local
文件:
SOME_VAR="this is very secret"
SOME_OTHER_VAR="this is not so secret, but needs to be different during tests"
有什么方法可以通过编程方式获取打字稿来制作这样的类型:
type TEnv = 'SOME_VAR' | 'SOME_OTHER_VAR'
我的用例是这样的
export default function env(key: TEnv): string {
return process.env[key] || Cypress.env(key)
}
我想我也许可以触发一个脚本,通过 dotenv
(或 bash)读取密钥,然后用它们写入一个 types.d.ts 文件...但是只是想听听是否已经有 TS 方法来做到这一点?
我最近的尝试是这样的
import dotenv from 'dotenv'
class DynamicArray<T> {
add(value: T): Array<T> {
let collection = new Array<T>()
collection.push(value)
return collection
}
}
const TEnvKey = new DynamicArray<string>()
const envFile = dotenv.config({ path: '.env.local' }).parsed
Object.keys(envFile).forEach((key) => TEnvKey.add(key))
export default function env(variable: TEnvKey): string {
return process.env[variable] || Cypress.env(variable)
}
但这不起作用..
旁注
NextJS 使用的 webpack 不允许 process.env[key]
并且 Cypress 会抛出引用错误。所以不得不这样做:
export default function env(key: TEnvKey): string {
try {
// have to try catch because of Cypress reference error
return Cypress.env(key)
} catch {
switch (key) {
case 'SOME_VAR':
return process.env.SOME_VAR
case 'SOME_OTHER_VAR':
return process.env.SOME_OTHER_VAR
default:
return undefined
}
}
}
我做了一个有用的东西:)
envTypeWriter.mjs
import dotenv from 'dotenv'
import fs from 'fs'
const env = dotenv.config({ path: '.env.local' }).parsed
const typeText = `type TEnvKey =\n | "${Object.keys(env).join('"\n | "')}"`
fs.writeFileSync('env.d.ts', typeText)
package.json
"scripts": {
...
"types": "node envTypeWriter.mjs"
},
然后我就在vscode中做yarn types
(和>Reload Window
)。
不要经常更改我的变量,这样就可以了。
更新版本
模板:
utils/env.ts
export default function env(key: TEnvKey): string {
try {
// have to try catch because of Cypress reference error
return Cypress.env(key);
} catch {
switch (key) {
// START OF AUTO GENERATED CASES -- DO NOT REMOVE OR EDIT THIS COMMENT
// this part will be replaced
// END OF AUTO GENERATED CASES -- DO NOT REMOVE OR EDIT THIS COMMENT
default:
return undefined;
}
}
}
// START OF AUTO GENERATED TYPES -- DO NOT REMOVE OR EDIT THIS COMMENT
// this will be replaced
envTypesWriter.ts
import { config } from 'dotenv'
import { writeFileSync, readFileSync } from 'fs'
const envLocal = config({ path: '.env.local' }).parsed // NOTE: Script has to be executed from the project root
const typeText = `type TEnvKey =\n | "${Object.keys(envLocal).join('"\n | "')}"`
const casesText = Object.keys(envLocal).reduce((acc, key) => {
acc += ` case '${key}':\n`
acc += ` return process.env.${key}\n`
return acc
}, '')
const filePath = 'utils/env.ts'
let text = readFileSync(filePath, { encoding: 'utf8' })
console.log(`reading ${filePath}`)
if (!text) throw 'nothing read'
const casesStartLineMarker =
'// START OF AUTO GENERATED CASES -- DO NOT REMOVE OR EDIT THIS COMMENT\n'
const casesEndLineMarker =
' // END OF AUTO GENERATED CASES -- DO NOT REMOVE OR EDIT THIS COMMENT\n'
text = `${
text.split(casesStartLineMarker)[0]
}${casesStartLineMarker}${casesText}${casesEndLineMarker}${
text.split(casesStartLineMarker)[1].split(casesEndLineMarker)[1]
}`
const typesLineMarker = '// START OF AUTO GENERATED TYPES -- DO NOT REMOVE OR EDIT THIS COMMENT\n'
const [untouchedCode, _types] = text.split(typesLineMarker)
const updatedText = `${untouchedCode}${typesLineMarker}${typeText}`
writeFileSync(filePath, updatedText, 'utf8')
假设我得到了这个 .env.local
文件:
SOME_VAR="this is very secret"
SOME_OTHER_VAR="this is not so secret, but needs to be different during tests"
有什么方法可以通过编程方式获取打字稿来制作这样的类型:
type TEnv = 'SOME_VAR' | 'SOME_OTHER_VAR'
我的用例是这样的
export default function env(key: TEnv): string {
return process.env[key] || Cypress.env(key)
}
我想我也许可以触发一个脚本,通过 dotenv
(或 bash)读取密钥,然后用它们写入一个 types.d.ts 文件...但是只是想听听是否已经有 TS 方法来做到这一点?
我最近的尝试是这样的
import dotenv from 'dotenv'
class DynamicArray<T> {
add(value: T): Array<T> {
let collection = new Array<T>()
collection.push(value)
return collection
}
}
const TEnvKey = new DynamicArray<string>()
const envFile = dotenv.config({ path: '.env.local' }).parsed
Object.keys(envFile).forEach((key) => TEnvKey.add(key))
export default function env(variable: TEnvKey): string {
return process.env[variable] || Cypress.env(variable)
}
但这不起作用..
旁注
NextJS 使用的 webpack 不允许 process.env[key]
并且 Cypress 会抛出引用错误。所以不得不这样做:
export default function env(key: TEnvKey): string {
try {
// have to try catch because of Cypress reference error
return Cypress.env(key)
} catch {
switch (key) {
case 'SOME_VAR':
return process.env.SOME_VAR
case 'SOME_OTHER_VAR':
return process.env.SOME_OTHER_VAR
default:
return undefined
}
}
}
我做了一个有用的东西:)
envTypeWriter.mjs
import dotenv from 'dotenv'
import fs from 'fs'
const env = dotenv.config({ path: '.env.local' }).parsed
const typeText = `type TEnvKey =\n | "${Object.keys(env).join('"\n | "')}"`
fs.writeFileSync('env.d.ts', typeText)
package.json
"scripts": {
...
"types": "node envTypeWriter.mjs"
},
然后我就在vscode中做yarn types
(和>Reload Window
)。
不要经常更改我的变量,这样就可以了。
更新版本
模板:
utils/env.ts
export default function env(key: TEnvKey): string {
try {
// have to try catch because of Cypress reference error
return Cypress.env(key);
} catch {
switch (key) {
// START OF AUTO GENERATED CASES -- DO NOT REMOVE OR EDIT THIS COMMENT
// this part will be replaced
// END OF AUTO GENERATED CASES -- DO NOT REMOVE OR EDIT THIS COMMENT
default:
return undefined;
}
}
}
// START OF AUTO GENERATED TYPES -- DO NOT REMOVE OR EDIT THIS COMMENT
// this will be replaced
envTypesWriter.ts
import { config } from 'dotenv'
import { writeFileSync, readFileSync } from 'fs'
const envLocal = config({ path: '.env.local' }).parsed // NOTE: Script has to be executed from the project root
const typeText = `type TEnvKey =\n | "${Object.keys(envLocal).join('"\n | "')}"`
const casesText = Object.keys(envLocal).reduce((acc, key) => {
acc += ` case '${key}':\n`
acc += ` return process.env.${key}\n`
return acc
}, '')
const filePath = 'utils/env.ts'
let text = readFileSync(filePath, { encoding: 'utf8' })
console.log(`reading ${filePath}`)
if (!text) throw 'nothing read'
const casesStartLineMarker =
'// START OF AUTO GENERATED CASES -- DO NOT REMOVE OR EDIT THIS COMMENT\n'
const casesEndLineMarker =
' // END OF AUTO GENERATED CASES -- DO NOT REMOVE OR EDIT THIS COMMENT\n'
text = `${
text.split(casesStartLineMarker)[0]
}${casesStartLineMarker}${casesText}${casesEndLineMarker}${
text.split(casesStartLineMarker)[1].split(casesEndLineMarker)[1]
}`
const typesLineMarker = '// START OF AUTO GENERATED TYPES -- DO NOT REMOVE OR EDIT THIS COMMENT\n'
const [untouchedCode, _types] = text.split(typesLineMarker)
const updatedText = `${untouchedCode}${typesLineMarker}${typeText}`
writeFileSync(filePath, updatedText, 'utf8')