diff --git a/src/index.ts b/src/index.ts
index da8d79f..4a1859b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -409,7 +409,9 @@ function typeFromSchema(schema: any, isSubType = false): string {
       case 'object':
         if (schema.properties) {
           const propertyKeyValues = Object.entries(schema.properties).map(([key, value]) => {
-            return `'${key}': ${typeFromSchema(value, true)}`
+            const isRequired = schema.required?.includes(key)
+            const optionalChar = isRequired ? '' : '?'
+            return `'${key}'${optionalChar}: ${typeFromSchema(value, true)}`
           })
 
           types.push(`{ ${propertyKeyValues.join('; ')} }`)
diff --git a/test/output/i18n-ally.ts b/test/output/i18n-ally.ts
index 6591e00..7f0d729 100644
--- a/test/output/i18n-ally.ts
+++ b/test/output/i18n-ally.ts
@@ -419,7 +419,7 @@ export interface ConfigKeyTypeMap {
   "i18n-ally.regex.key": (string | undefined),
   "i18n-ally.regex.usageMatch": (string[] | undefined),
   "i18n-ally.regex.usageMatchAppend": (string[] | undefined),
-  "i18n-ally.refactor.templates": ({ 'source': ("html-attribute" | "html-inline" | "js-string" | "js-template" | "jsx-text"); 'template': string; 'templates': string[]; 'include': string[]; 'exclude': string[] }[] | undefined),
+  "i18n-ally.refactor.templates": ({ 'source'?: ("html-attribute" | "html-inline" | "js-string" | "js-template" | "jsx-text"); 'template'?: string; 'templates'?: string[]; 'include'?: string[]; 'exclude'?: string[] }[] | undefined),
   "i18n-ally.translate.saveAsCandidates": boolean,
   "i18n-ally.translate.fallbackToKey": boolean,
   "i18n-ally.translate.engines": ("google" | "google-cn" | "deepl" | "libretranslate" | "baidu" | "openai")[],
@@ -627,7 +627,7 @@ export interface ConfigShorthandTypeMap {
   regexKey: (string | undefined),
   regexUsageMatch: (string[] | undefined),
   regexUsageMatchAppend: (string[] | undefined),
-  refactorTemplates: ({ 'source': ("html-attribute" | "html-inline" | "js-string" | "js-template" | "jsx-text"); 'template': string; 'templates': string[]; 'include': string[]; 'exclude': string[] }[] | undefined),
+  refactorTemplates: ({ 'source'?: ("html-attribute" | "html-inline" | "js-string" | "js-template" | "jsx-text"); 'template'?: string; 'templates'?: string[]; 'include'?: string[]; 'exclude'?: string[] }[] | undefined),
   translateSaveAsCandidates: boolean,
   translateFallbackToKey: boolean,
   translateEngines: ("google" | "google-cn" | "deepl" | "libretranslate" | "baidu" | "openai")[],
@@ -1753,7 +1753,7 @@ export interface ScopedConfigKeyTypeMap {
   "regex.key": (string | undefined),
   "regex.usageMatch": (string[] | undefined),
   "regex.usageMatchAppend": (string[] | undefined),
-  "refactor.templates": ({ 'source': ("html-attribute" | "html-inline" | "js-string" | "js-template" | "jsx-text"); 'template': string; 'templates': string[]; 'include': string[]; 'exclude': string[] }[] | undefined),
+  "refactor.templates": ({ 'source'?: ("html-attribute" | "html-inline" | "js-string" | "js-template" | "jsx-text"); 'template'?: string; 'templates'?: string[]; 'include'?: string[]; 'exclude'?: string[] }[] | undefined),
   "translate.saveAsCandidates": boolean,
   "translate.fallbackToKey": boolean,
   "translate.engines": ("google" | "google-cn" | "deepl" | "libretranslate" | "baidu" | "openai")[],
@@ -1938,7 +1938,7 @@ export interface NestedConfigs {
       "usageMatchAppend": (string[] | undefined),
     },
     "refactor": {
-      "templates": ({ 'source': ("html-attribute" | "html-inline" | "js-string" | "js-template" | "jsx-text"); 'template': string; 'templates': string[]; 'include': string[]; 'exclude': string[] }[] | undefined),
+      "templates": ({ 'source'?: ("html-attribute" | "html-inline" | "js-string" | "js-template" | "jsx-text"); 'template'?: string; 'templates'?: string[]; 'include'?: string[]; 'exclude'?: string[] }[] | undefined),
     },
     "translate": {
       "saveAsCandidates": boolean,
@@ -2083,7 +2083,7 @@ export interface NestedScopedConfigs {
     "usageMatchAppend": (string[] | undefined),
   },
   "refactor": {
-    "templates": ({ 'source': ("html-attribute" | "html-inline" | "js-string" | "js-template" | "jsx-text"); 'template': string; 'templates': string[]; 'include': string[]; 'exclude': string[] }[] | undefined),
+    "templates": ({ 'source'?: ("html-attribute" | "html-inline" | "js-string" | "js-template" | "jsx-text"); 'template'?: string; 'templates'?: string[]; 'include'?: string[]; 'exclude'?: string[] }[] | undefined),
   },
   "translate": {
     "saveAsCandidates": boolean,
diff --git a/test/output/vscode-smart-clicks.ts b/test/output/vscode-smart-clicks.ts
index 56114cb..962f58a 100644
--- a/test/output/vscode-smart-clicks.ts
+++ b/test/output/vscode-smart-clicks.ts
@@ -39,7 +39,7 @@ export interface ConfigKeyTypeMap {
   "smartClicks.clicksInterval": number,
   "smartClicks.triggerDelay": number,
   "smartClicks.htmlLanguageIds": string[],
-  "smartClicks.rules": { 'bracket-pair': boolean; 'dash': boolean; 'html-attr': boolean; 'html-element': boolean; 'html-tag-pair': boolean; 'js-arrow-fn': boolean; 'js-assign': boolean; 'js-block': boolean; 'js-colon': boolean; 'jsx-tag-pair': boolean },
+  "smartClicks.rules": { 'bracket-pair'?: boolean; 'dash'?: boolean; 'html-attr'?: boolean; 'html-element'?: boolean; 'html-tag-pair'?: boolean; 'js-arrow-fn'?: boolean; 'js-assign'?: boolean; 'js-block'?: boolean; 'js-colon'?: boolean; 'jsx-tag-pair'?: boolean },
 }
 
 export interface ConfigShorthandMap {
@@ -53,7 +53,7 @@ export interface ConfigShorthandTypeMap {
   clicksInterval: number,
   triggerDelay: number,
   htmlLanguageIds: string[],
-  rules: { 'bracket-pair': boolean; 'dash': boolean; 'html-attr': boolean; 'html-element': boolean; 'html-tag-pair': boolean; 'js-arrow-fn': boolean; 'js-assign': boolean; 'js-block': boolean; 'js-colon': boolean; 'jsx-tag-pair': boolean },
+  rules: { 'bracket-pair'?: boolean; 'dash'?: boolean; 'html-attr'?: boolean; 'html-element'?: boolean; 'html-tag-pair'?: boolean; 'js-arrow-fn'?: boolean; 'js-assign'?: boolean; 'js-block'?: boolean; 'js-colon'?: boolean; 'jsx-tag-pair'?: boolean },
 }
 
 export interface ConfigItem<T extends keyof ConfigKeyTypeMap> {
@@ -112,7 +112,7 @@ export interface ScopedConfigKeyTypeMap {
   "clicksInterval": number,
   "triggerDelay": number,
   "htmlLanguageIds": string[],
-  "rules": { 'bracket-pair': boolean; 'dash': boolean; 'html-attr': boolean; 'html-element': boolean; 'html-tag-pair': boolean; 'js-arrow-fn': boolean; 'js-assign': boolean; 'js-block': boolean; 'js-colon': boolean; 'jsx-tag-pair': boolean },
+  "rules": { 'bracket-pair'?: boolean; 'dash'?: boolean; 'html-attr'?: boolean; 'html-element'?: boolean; 'html-tag-pair'?: boolean; 'js-arrow-fn'?: boolean; 'js-assign'?: boolean; 'js-block'?: boolean; 'js-colon'?: boolean; 'jsx-tag-pair'?: boolean },
 }
 
 export const scopedConfigs = {
@@ -130,7 +130,7 @@ export interface NestedConfigs {
     "clicksInterval": number,
     "triggerDelay": number,
     "htmlLanguageIds": string[],
-    "rules": { 'bracket-pair': boolean; 'dash': boolean; 'html-attr': boolean; 'html-element': boolean; 'html-tag-pair': boolean; 'js-arrow-fn': boolean; 'js-assign': boolean; 'js-block': boolean; 'js-colon': boolean; 'jsx-tag-pair': boolean },
+    "rules": { 'bracket-pair'?: boolean; 'dash'?: boolean; 'html-attr'?: boolean; 'html-element'?: boolean; 'html-tag-pair'?: boolean; 'js-arrow-fn'?: boolean; 'js-assign'?: boolean; 'js-block'?: boolean; 'js-colon'?: boolean; 'jsx-tag-pair'?: boolean },
   },
 }
 
@@ -138,6 +138,6 @@ export interface NestedScopedConfigs {
   "clicksInterval": number,
   "triggerDelay": number,
   "htmlLanguageIds": string[],
-  "rules": { 'bracket-pair': boolean; 'dash': boolean; 'html-attr': boolean; 'html-element': boolean; 'html-tag-pair': boolean; 'js-arrow-fn': boolean; 'js-assign': boolean; 'js-block': boolean; 'js-colon': boolean; 'jsx-tag-pair': boolean },
+  "rules": { 'bracket-pair'?: boolean; 'dash'?: boolean; 'html-attr'?: boolean; 'html-element'?: boolean; 'html-tag-pair'?: boolean; 'js-arrow-fn'?: boolean; 'js-assign'?: boolean; 'js-block'?: boolean; 'js-colon'?: boolean; 'jsx-tag-pair'?: boolean },
 }