src/vs/workbench/services/preferences/common/preferencesValidation.ts/getObjectValidator
function getObjectValidator(prop: IConfigurationPropertySchema): ((value: any) => (string | null)) | null {
	if (prop.type === 'object') {
		const { properties, patternProperties, additionalProperties } = prop;
		return value => {
			if (!value) {
				return null;
			}

			const errors: string[] = [];

			if (!isObject(value)) {
				errors.push(nls.localize('validations.objectIncorrectType', 'Incorrect type. Expected an object.'));
			} else {
				Object.keys(value).forEach((key: string) => {
					const data = value[key];
					if (properties && key in properties) {
						const errorMessage = getErrorsForSchema(properties[key], data);
						if (errorMessage) {
							errors.push(`${key}: ${errorMessage}\n`);
						}
						return;
					}

					if (patternProperties) {
						for (const pattern in patternProperties) {
							if (RegExp(pattern).test(key)) {
								const errorMessage = getErrorsForSchema(patternProperties[pattern], data);
								if (errorMessage) {
									errors.push(`${key}: ${errorMessage}\n`);
								}
								return;
							}
						}
					}

					if (additionalProperties === false) {
						errors.push(nls.localize('validations.objectPattern', 'Property {0} is not allowed.\n', key));
					} else if (typeof additionalProperties === 'object') {
						const errorMessage = getErrorsForSchema(additionalProperties, data);
						if (errorMessage) {
							errors.push(`${key}: ${errorMessage}\n`);
						}
					}
				});
			}

			if (errors.length) {
				return prop.errorMessage ? [prop.errorMessage, ...errors].join(' ') : errors.join(' ');
			}

			return '';
		};
	}

	return null;
}